diff options
Diffstat (limited to 'security/tomoyo')
| -rw-r--r-- | security/tomoyo/.gitignore | 2 | ||||
| -rw-r--r-- | security/tomoyo/Kconfig | 63 | ||||
| -rw-r--r-- | security/tomoyo/Makefile | 49 | ||||
| -rw-r--r-- | security/tomoyo/audit.c | 470 | ||||
| -rw-r--r-- | security/tomoyo/common.c | 2277 | ||||
| -rw-r--r-- | security/tomoyo/common.h | 1377 | ||||
| -rw-r--r-- | security/tomoyo/condition.c | 1094 | ||||
| -rw-r--r-- | security/tomoyo/domain.c | 817 | ||||
| -rw-r--r-- | security/tomoyo/environ.c | 122 | ||||
| -rw-r--r-- | security/tomoyo/file.c | 1000 | ||||
| -rw-r--r-- | security/tomoyo/gc.c | 677 | ||||
| -rw-r--r-- | security/tomoyo/group.c | 122 | ||||
| -rw-r--r-- | security/tomoyo/load_policy.c | 82 | ||||
| -rw-r--r-- | security/tomoyo/memory.c | 183 | ||||
| -rw-r--r-- | security/tomoyo/mount.c | 212 | ||||
| -rw-r--r-- | security/tomoyo/network.c | 771 | ||||
| -rw-r--r-- | security/tomoyo/realpath.c | 271 | ||||
| -rw-r--r-- | security/tomoyo/securityfs_if.c | 157 | ||||
| -rw-r--r-- | security/tomoyo/tomoyo.c | 334 | ||||
| -rw-r--r-- | security/tomoyo/util.c | 454 | 
20 files changed, 7634 insertions, 2900 deletions
diff --git a/security/tomoyo/.gitignore b/security/tomoyo/.gitignore new file mode 100644 index 00000000000..5caf1a6f590 --- /dev/null +++ b/security/tomoyo/.gitignore @@ -0,0 +1,2 @@ +builtin-policy.h +policy/ diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index c8f38579323..8eb779b9d77 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -1,11 +1,74 @@  config SECURITY_TOMOYO  	bool "TOMOYO Linux Support"  	depends on SECURITY +	depends on NET  	select SECURITYFS  	select SECURITY_PATH +	select SECURITY_NETWORK  	default n  	help  	  This selects TOMOYO Linux, pathname-based access control.  	  Required userspace tools and further information may be  	  found at <http://tomoyo.sourceforge.jp/>.  	  If you are unsure how to answer this question, answer N. + +config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY +	int "Default maximal count for learning mode" +	default 2048 +	range 0 2147483647 +	depends on SECURITY_TOMOYO +	help +	  This is the default value for maximal ACL entries +	  that are automatically appended into policy at "learning mode". +	  Some programs access thousands of objects, so running +	  such programs in "learning mode" dulls the system response +	  and consumes much memory. +	  This is the safeguard for such programs. + +config SECURITY_TOMOYO_MAX_AUDIT_LOG +	int "Default maximal count for audit log" +	default 1024 +	range 0 2147483647 +	depends on SECURITY_TOMOYO +	help +	  This is the default value for maximal entries for +	  audit logs that the kernel can hold on memory. +	  You can read the log via /sys/kernel/security/tomoyo/audit. +	  If you don't need audit logs, you may set this value to 0. + +config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER +	bool "Activate without calling userspace policy loader." +	default n +	depends on SECURITY_TOMOYO +	---help--- +	  Say Y here if you want to activate access control as soon as built-in +	  policy was loaded. This option will be useful for systems where +	  operations which can lead to the hijacking of the boot sequence are +	  needed before loading the policy. For example, you can activate +	  immediately after loading the fixed part of policy which will allow +	  only operations needed for mounting a partition which contains the +	  variant part of policy and verifying (e.g. running GPG check) and +	  loading the variant part of policy. Since you can start using +	  enforcing mode from the beginning, you can reduce the possibility of +	  hijacking the boot sequence. + +config SECURITY_TOMOYO_POLICY_LOADER +	string "Location of userspace policy loader" +	default "/sbin/tomoyo-init" +	depends on SECURITY_TOMOYO +	depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER +	---help--- +	  This is the default pathname of policy loader which is called before +	  activation. You can override this setting via TOMOYO_loader= kernel +	  command line option. + +config SECURITY_TOMOYO_ACTIVATION_TRIGGER +	string "Trigger for calling userspace policy loader" +	default "/sbin/init" +	depends on SECURITY_TOMOYO +	depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER +	---help--- +	  This is the default pathname of activation trigger. +	  You can override this setting via TOMOYO_trigger= kernel command line +	  option. For example, if you pass init=/bin/systemd option, you may +	  want to also pass TOMOYO_trigger=/bin/systemd option. diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 91640e96bd0..56a0c7be409 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1,48 @@ -obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o +obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o + +$(obj)/policy/profile.conf: +	@mkdir -p $(obj)/policy/ +	@echo Creating an empty policy/profile.conf +	@touch $@ + +$(obj)/policy/exception_policy.conf: +	@mkdir -p $(obj)/policy/ +	@echo Creating a default policy/exception_policy.conf +	@echo initialize_domain /sbin/modprobe from any >> $@ +	@echo initialize_domain /sbin/hotplug from any >> $@ + +$(obj)/policy/domain_policy.conf: +	@mkdir -p $(obj)/policy/ +	@echo Creating an empty policy/domain_policy.conf +	@touch $@ + +$(obj)/policy/manager.conf: +	@mkdir -p $(obj)/policy/ +	@echo Creating an empty policy/manager.conf +	@touch $@ + +$(obj)/policy/stat.conf: +	@mkdir -p $(obj)/policy/ +	@echo Creating an empty policy/stat.conf +	@touch $@ + +$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf +	@echo Generating built-in policy for TOMOYO 2.5.x. +	@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp +	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp +	@echo "\"\";" >> $@.tmp +	@echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp +	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp +	@echo "\"\";" >> $@.tmp +	@echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp +	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp +	@echo "\"\";" >> $@.tmp +	@echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp +	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp +	@echo "\"\";" >> $@.tmp +	@echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp +	@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp +	@echo "\"\";" >> $@.tmp +	@mv $@.tmp $@ + +$(obj)/common.o: $(obj)/builtin-policy.h diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c new file mode 100644 index 00000000000..c1b00375c9a --- /dev/null +++ b/security/tomoyo/audit.c @@ -0,0 +1,470 @@ +/* + * security/tomoyo/audit.c + * + * Copyright (C) 2005-2011  NTT DATA CORPORATION + */ + +#include "common.h" +#include <linux/slab.h> + +/** + * tomoyo_print_bprm - Print "struct linux_binprm" for auditing. + * + * @bprm: Pointer to "struct linux_binprm". + * @dump: Pointer to "struct tomoyo_page_dump". + * + * Returns the contents of @bprm on success, NULL otherwise. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_print_bprm(struct linux_binprm *bprm, +			       struct tomoyo_page_dump *dump) +{ +	static const int tomoyo_buffer_len = 4096 * 2; +	char *buffer = kzalloc(tomoyo_buffer_len, GFP_NOFS); +	char *cp; +	char *last_start; +	int len; +	unsigned long pos = bprm->p; +	int offset = pos % PAGE_SIZE; +	int argv_count = bprm->argc; +	int envp_count = bprm->envc; +	bool truncated = false; +	if (!buffer) +		return NULL; +	len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ "); +	cp = buffer + len; +	if (!argv_count) { +		memmove(cp, "} envp[]={ ", 11); +		cp += 11; +	} +	last_start = cp; +	while (argv_count || envp_count) { +		if (!tomoyo_dump_page(bprm, pos, dump)) +			goto out; +		pos += PAGE_SIZE - offset; +		/* Read. */ +		while (offset < PAGE_SIZE) { +			const char *kaddr = dump->data; +			const unsigned char c = kaddr[offset++]; +			if (cp == last_start) +				*cp++ = '"'; +			if (cp >= buffer + tomoyo_buffer_len - 32) { +				/* Reserve some room for "..." string. */ +				truncated = true; +			} else if (c == '\\') { +				*cp++ = '\\'; +				*cp++ = '\\'; +			} else if (c > ' ' && c < 127) { +				*cp++ = c; +			} else if (!c) { +				*cp++ = '"'; +				*cp++ = ' '; +				last_start = cp; +			} else { +				*cp++ = '\\'; +				*cp++ = (c >> 6) + '0'; +				*cp++ = ((c >> 3) & 7) + '0'; +				*cp++ = (c & 7) + '0'; +			} +			if (c) +				continue; +			if (argv_count) { +				if (--argv_count == 0) { +					if (truncated) { +						cp = last_start; +						memmove(cp, "... ", 4); +						cp += 4; +					} +					memmove(cp, "} envp[]={ ", 11); +					cp += 11; +					last_start = cp; +					truncated = false; +				} +			} else if (envp_count) { +				if (--envp_count == 0) { +					if (truncated) { +						cp = last_start; +						memmove(cp, "... ", 4); +						cp += 4; +					} +				} +			} +			if (!argv_count && !envp_count) +				break; +		} +		offset = 0; +	} +	*cp++ = '}'; +	*cp = '\0'; +	return buffer; +out: +	snprintf(buffer, tomoyo_buffer_len - 1, +		 "argv[]={ ... } envp[]= { ... }"); +	return buffer; +} + +/** + * tomoyo_filetype - Get string representation of file type. + * + * @mode: Mode value for stat(). + * + * Returns file type string. + */ +static inline const char *tomoyo_filetype(const umode_t mode) +{ +	switch (mode & S_IFMT) { +	case S_IFREG: +	case 0: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE]; +	case S_IFDIR: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY]; +	case S_IFLNK: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK]; +	case S_IFIFO: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO]; +	case S_IFSOCK: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET]; +	case S_IFBLK: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV]; +	case S_IFCHR: +		return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV]; +	} +	return "unknown"; /* This should not happen. */ +} + +/** + * 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 tomoyo_time stamp; +	const pid_t gpid = task_pid_nr(current); +	struct tomoyo_obj_info *obj = r->obj; +	static const int tomoyo_buffer_len = 4096; +	char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); +	int pos; +	u8 i; +	if (!buffer) +		return NULL; +	{ +		struct timeval tv; +		do_gettimeofday(&tv); +		tomoyo_convert_time(tv.tv_sec, &stamp); +	} +	pos = snprintf(buffer, tomoyo_buffer_len - 1, +		       "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s " +		       "granted=%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 }", stamp.year, stamp.month, +		       stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile, +		       tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid, +		       tomoyo_sys_getpid(), tomoyo_sys_getppid(), +		       from_kuid(&init_user_ns, current_uid()), +		       from_kgid(&init_user_ns, current_gid()), +		       from_kuid(&init_user_ns, current_euid()), +		       from_kgid(&init_user_ns, current_egid()), +		       from_kuid(&init_user_ns, current_suid()), +		       from_kgid(&init_user_ns, current_sgid()), +		       from_kuid(&init_user_ns, current_fsuid()), +		       from_kgid(&init_user_ns, current_fsgid())); +	if (!obj) +		goto no_obj_info; +	if (!obj->validate_done) { +		tomoyo_get_attributes(obj); +		obj->validate_done = true; +	} +	for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) { +		struct tomoyo_mini_stat *stat; +		unsigned int dev; +		umode_t mode; +		if (!obj->stat_valid[i]) +			continue; +		stat = &obj->stat[i]; +		dev = stat->dev; +		mode = stat->mode; +		if (i & 1) { +			pos += snprintf(buffer + pos, +					tomoyo_buffer_len - 1 - pos, +					" path%u.parent={ uid=%u gid=%u " +					"ino=%lu perm=0%o }", (i >> 1) + 1, +					from_kuid(&init_user_ns, stat->uid), +					from_kgid(&init_user_ns, stat->gid), +					(unsigned long)stat->ino, +					stat->mode & S_IALLUGO); +			continue; +		} +		pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, +				" path%u={ uid=%u gid=%u ino=%lu major=%u" +				" minor=%u perm=0%o type=%s", (i >> 1) + 1, +				from_kuid(&init_user_ns, stat->uid), +				from_kgid(&init_user_ns, stat->gid), +				(unsigned long)stat->ino, +				MAJOR(dev), MINOR(dev), +				mode & S_IALLUGO, tomoyo_filetype(mode)); +		if (S_ISCHR(mode) || S_ISBLK(mode)) { +			dev = stat->rdev; +			pos += snprintf(buffer + pos, +					tomoyo_buffer_len - 1 - pos, +					" dev_major=%u dev_minor=%u", +					MAJOR(dev), MINOR(dev)); +		} +		pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, +				" }"); +	} +no_obj_info: +	if (pos < tomoyo_buffer_len - 1) +		return buffer; +	kfree(buffer); +	return NULL; +} + +/** + * tomoyo_init_log - Allocate buffer for audit logs. + * + * @r:    Pointer to "struct tomoyo_request_info". + * @len:  Buffer size needed for @fmt and @args. + * @fmt:  The printf()'s format string. + * @args: va_list structure for @fmt. + * + * Returns pointer to allocated memory. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, +		      va_list args) +{ +	char *buf = NULL; +	char *bprm_info = NULL; +	const char *header = NULL; +	char *realpath = NULL; +	const char *symlink = NULL; +	int pos; +	const char *domainname = r->domain->domainname->name; +	header = tomoyo_print_header(r); +	if (!header) +		return NULL; +	/* +10 is for '\n' etc. and '\0'. */ +	len += strlen(domainname) + strlen(header) + 10; +	if (r->ee) { +		struct file *file = r->ee->bprm->file; +		realpath = tomoyo_realpath_from_path(&file->f_path); +		bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump); +		if (!realpath || !bprm_info) +			goto out; +		/* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */ +		len += strlen(realpath) + 80 + strlen(bprm_info); +	} else if (r->obj && r->obj->symlink_target) { +		symlink = r->obj->symlink_target->name; +		/* +18 is for " symlink.target=\"%s\"" */ +		len += 18 + strlen(symlink); +	} +	len = tomoyo_round2(len); +	buf = kzalloc(len, GFP_NOFS); +	if (!buf) +		goto out; +	len--; +	pos = snprintf(buf, len, "%s", header); +	if (realpath) { +		struct linux_binprm *bprm = r->ee->bprm; +		pos += snprintf(buf + pos, len - pos, +				" exec={ realpath=\"%s\" argc=%d envc=%d %s }", +				realpath, bprm->argc, bprm->envc, bprm_info); +	} else if (symlink) +		pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"", +				symlink); +	pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname); +	vsnprintf(buf + pos, len - pos, fmt, args); +out: +	kfree(realpath); +	kfree(bprm_info); +	kfree(header); +	return buf; +} + +/* Wait queue for /sys/kernel/security/tomoyo/audit. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait); + +/* Structure for audit log. */ +struct tomoyo_log { +	struct list_head list; +	char *log; +	int size; +}; + +/* The list for "struct tomoyo_log". */ +static LIST_HEAD(tomoyo_log); + +/* Lock for "struct list_head tomoyo_log". */ +static DEFINE_SPINLOCK(tomoyo_log_lock); + +/* Length of "stuct list_head tomoyo_log". */ +static unsigned int tomoyo_log_count; + +/** + * tomoyo_get_audit - Get audit mode. + * + * @ns:          Pointer to "struct tomoyo_policy_namespace". + * @profile:     Profile number. + * @index:       Index number of functionality. + * @is_granted:  True if granted log, false otherwise. + * + * Returns true if this request should be audited, false otherwise. + */ +static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, +			     const u8 profile, const u8 index, +			     const struct tomoyo_acl_info *matched_acl, +			     const bool is_granted) +{ +	u8 mode; +	const u8 category = tomoyo_index2category[index] + +		TOMOYO_MAX_MAC_INDEX; +	struct tomoyo_profile *p; +	if (!tomoyo_policy_loaded) +		return false; +	p = tomoyo_profile(ns, profile); +	if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG]) +		return false; +	if (is_granted && matched_acl && matched_acl->cond && +	    matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO) +		return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES; +	mode = p->config[index]; +	if (mode == TOMOYO_CONFIG_USE_DEFAULT) +		mode = p->config[category]; +	if (mode == TOMOYO_CONFIG_USE_DEFAULT) +		mode = p->default_config; +	if (is_granted) +		return mode & TOMOYO_CONFIG_WANT_GRANT_LOG; +	return mode & TOMOYO_CONFIG_WANT_REJECT_LOG; +} + +/** + * tomoyo_write_log2 - Write an audit log. + * + * @r:    Pointer to "struct tomoyo_request_info". + * @len:  Buffer size needed for @fmt and @args. + * @fmt:  The printf()'s format string. + * @args: va_list structure for @fmt. + * + * Returns nothing. + */ +void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, +		       va_list args) +{ +	char *buf; +	struct tomoyo_log *entry; +	bool quota_exceeded = false; +	if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, +			      r->matched_acl, r->granted)) +		goto out; +	buf = tomoyo_init_log(r, len, fmt, args); +	if (!buf) +		goto out; +	entry = kzalloc(sizeof(*entry), GFP_NOFS); +	if (!entry) { +		kfree(buf); +		goto out; +	} +	entry->log = buf; +	len = tomoyo_round2(strlen(buf) + 1); +	/* +	 * The entry->size is used for memory quota checks. +	 * Don't go beyond strlen(entry->log). +	 */ +	entry->size = len + tomoyo_round2(sizeof(*entry)); +	spin_lock(&tomoyo_log_lock); +	if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] && +	    tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >= +	    tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) { +		quota_exceeded = true; +	} else { +		tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size; +		list_add_tail(&entry->list, &tomoyo_log); +		tomoyo_log_count++; +	} +	spin_unlock(&tomoyo_log_lock); +	if (quota_exceeded) { +		kfree(buf); +		kfree(entry); +		goto out; +	} +	wake_up(&tomoyo_log_wait); +out: +	return; +} + +/** + * tomoyo_write_log - Write an audit log. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + * + * Returns nothing. + */ +void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) +{ +	va_list args; +	int len; +	va_start(args, fmt); +	len = vsnprintf((char *) &len, 1, fmt, args) + 1; +	va_end(args); +	va_start(args, fmt); +	tomoyo_write_log2(r, len, fmt, args); +	va_end(args); +} + +/** + * tomoyo_read_log - Read an audit log. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +void tomoyo_read_log(struct tomoyo_io_buffer *head) +{ +	struct tomoyo_log *ptr = NULL; +	if (head->r.w_pos) +		return; +	kfree(head->read_buf); +	head->read_buf = NULL; +	spin_lock(&tomoyo_log_lock); +	if (!list_empty(&tomoyo_log)) { +		ptr = list_entry(tomoyo_log.next, typeof(*ptr), list); +		list_del(&ptr->list); +		tomoyo_log_count--; +		tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size; +	} +	spin_unlock(&tomoyo_log_lock); +	if (ptr) { +		head->read_buf = ptr->log; +		head->r.w[head->r.w_pos++] = head->read_buf; +		kfree(ptr); +	} +} + +/** + * tomoyo_poll_log - Wait for an audit log. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". Maybe NULL. + * + * Returns POLLIN | POLLRDNORM when ready to read an audit log. + */ +unsigned int tomoyo_poll_log(struct file *file, poll_table *wait) +{ +	if (tomoyo_log_count) +		return POLLIN | POLLRDNORM; +	poll_wait(file, &tomoyo_log_wait, wait); +	if (tomoyo_log_count) +		return POLLIN | POLLRDNORM; +	return 0; +} diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 7556315c197..283862aebdc 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,163 @@  #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] = { +	/* CONFIG::file group */ +	[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", +	/* CONFIG::network group */ +	[TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       = "inet_stream_bind", +	[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     = "inet_stream_listen", +	[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    = "inet_stream_connect", +	[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        = "inet_dgram_bind", +	[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        = "inet_dgram_send", +	[TOMOYO_MAC_NETWORK_INET_RAW_BIND]          = "inet_raw_bind", +	[TOMOYO_MAC_NETWORK_INET_RAW_SEND]          = "inet_raw_send", +	[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       = "unix_stream_bind", +	[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     = "unix_stream_listen", +	[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    = "unix_stream_connect", +	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        = "unix_dgram_bind", +	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        = "unix_dgram_send", +	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    = "unix_seqpacket_bind", +	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  = "unix_seqpacket_listen", +	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", +	/* CONFIG::misc group */ +	[TOMOYO_MAC_ENVIRON] = "env", +	/* CONFIG group */ +	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", +	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network", +	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", +}; -/* 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 socket's operation. */ +const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = { +	[TOMOYO_NETWORK_BIND]    = "bind", +	[TOMOYO_NETWORK_LISTEN]  = "listen", +	[TOMOYO_NETWORK_CONNECT] = "connect", +	[TOMOYO_NETWORK_SEND]    = "send", +}; + +/* String table for categories. */ +static const char * const tomoyo_category_keywords +[TOMOYO_MAX_MAC_CATEGORY_INDEX] = { +	[TOMOYO_MAC_CATEGORY_FILE]    = "file", +	[TOMOYO_MAC_CATEGORY_NETWORK] = "network", +	[TOMOYO_MAC_CATEGORY_MISC]    = "misc",  };  /* Permit policy management by non-root user? */ @@ -71,11 +178,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 +212,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; @@ -108,11 +224,10 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)  			head->read_user_buf += len;  			w += len;  		} -		if (*w) { -			head->r.w[0] = w; +		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)) @@ -147,17 +262,21 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)  		WARN_ON(1);  } +static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, +			     ...) __printf(2, 3); +  /**   * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.   *   * @head: Pointer to "struct tomoyo_io_buffer".   * @fmt:  The printf()'s format string, followed by parameters.   */ -void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) +static 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; @@ -172,11 +291,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"); @@ -184,6 +317,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 = 20110903; +	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". @@ -193,7 +382,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 { @@ -202,24 +391,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++) { @@ -233,8 +444,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) @@ -248,35 +459,55 @@ 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] = +			CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG; +		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = +			CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY;  		mb(); /* Avoid out-of-order execution. */ -		tomoyo_profile_ptr[profile] = ptr; +		ns->profile_ptr[profile] = ptr;  		entry = NULL;  	}  	mutex_unlock(&tomoyo_policy_lock); @@ -288,19 +519,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); @@ -314,18 +555,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)  { @@ -334,51 +572,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; @@ -390,7 +593,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; @@ -400,7 +613,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; @@ -411,6 +624,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; @@ -430,128 +661,121 @@ 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")) { -		const struct tomoyo_path_info *old_comment = profile->comment; -		profile->comment = tomoyo_get_name(cp); +		static DEFINE_SPINLOCK(lock); +		const struct tomoyo_path_info *new_comment +			= tomoyo_get_name(cp); +		const struct tomoyo_path_info *old_comment; +		if (!new_comment) +			return -ENOMEM; +		spin_lock(&lock); +		old_comment = profile->comment; +		profile->comment = new_comment; +		spin_unlock(&lock);  		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) +		if (head->r.index == TOMOYO_MAX_PROFILES) { +			head->r.eof = true;  			return; +		}  		head->r.step++;  		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_print_namespace(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; @@ -565,15 +789,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;  		} @@ -583,6 +814,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)  { @@ -604,23 +843,22 @@ static int tomoyo_update_manager_entry(const char *manager,  				       const bool is_delete)  {  	struct tomoyo_manager e = { }; -	int error; - -	if (tomoyo_domain_def(manager)) { -		if (!tomoyo_correct_domain(manager)) -			return -EINVAL; -		e.is_domain = true; -	} else { -		if (!tomoyo_correct_path(manager)) -			return -EINVAL; -	} +	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_correct_domain(manager) && +	    !tomoyo_correct_word(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;  } @@ -636,13 +874,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);  }  /** @@ -656,8 +893,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) @@ -688,25 +925,18 @@ static bool tomoyo_manager(void)  	if (!tomoyo_policy_loaded)  		return true; -	if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) +	if (!tomoyo_manage_by_non_root && +	    (!uid_eq(task->cred->uid,  GLOBAL_ROOT_UID) || +	     !uid_eq(task->cred->euid, GLOBAL_ROOT_UID)))  		return false; -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], -				head.list) { -		if (!ptr->head.is_deleted && ptr->is_domain -		    && !tomoyo_pathcmp(domainname, ptr->manager)) { -			found = true; -			break; -		} -	} -	if (found) -		return true;  	exe = tomoyo_get_exe();  	if (!exe)  		return false; -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], -				head.list) { -		if (!ptr->head.is_deleted && !ptr->is_domain -		    && !strcmp(exe, ptr->manager->name)) { +	list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. +				policy_list[TOMOYO_ID_MANAGER], head.list) { +		if (!ptr->head.is_deleted && +		    (!tomoyo_pathcmp(domainname, ptr->manager) || +		     !strcmp(exe, ptr->manager->name))) {  			found = true;  			break;  		} @@ -724,8 +954,11 @@ static bool tomoyo_manager(void)  	return found;  } +static struct tomoyo_domain_info *tomoyo_find_domain_by_qid +(unsigned int serial); +  /** - * tomoyo_select_one - Parse select command. + * tomoyo_select_domain - Parse select command.   *   * @head: Pointer to "struct tomoyo_io_buffer".   * @data: String to parse. @@ -734,35 +967,34 @@ 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;  		rcu_read_lock(); -		read_lock(&tasklist_lock);  		if (global_pid)  			p = find_task_by_pid_ns(pid, &init_pid_ns);  		else  			p = find_task_by_vpid(pid);  		if (p)  			domain = tomoyo_real_domain(p); -		read_unlock(&tasklist_lock);  		rcu_read_unlock();  	} else if (!strncmp(data, "domain=", 7)) {  		if (tomoyo_domain_def(data + 7))  			domain = tomoyo_find_domain(data + 7); +	} else if (sscanf(data, "Q=%u", &pid) == 1) { +		domain = tomoyo_find_domain_by_qid(pid);  	} 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). */ @@ -779,11 +1011,53 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)  }  /** + * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, +			      const struct tomoyo_acl_info *b) +{ +	const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head); +	const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head); +	return p1->domainname == p2->domainname; +} + +/** + * tomoyo_write_task - Update task related list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_task(struct tomoyo_acl_param *param) +{ +	int error = -EINVAL; +	if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) { +		struct tomoyo_task_acl e = { +			.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL, +			.domainname = tomoyo_get_domainname(param), +		}; +		if (e.domainname) +			error = tomoyo_update_domain(&e.head, sizeof(e), param, +						     tomoyo_same_task_acl, +						     NULL); +		tomoyo_put_name(e.domainname); +	} +	return error; +} + +/**   * tomoyo_delete_domain - Delete a domain.   *   * @domainname: The name of domain.   * - * Returns 0. + * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ @@ -795,7 +1069,7 @@ static int tomoyo_delete_domain(char *domainname)  	name.name = domainname;  	tomoyo_fill_path_info(&name);  	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		return 0; +		return -EINTR;  	/* Is there an active domain? */  	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {  		/* Never delete tomoyo_kernel_domain */ @@ -814,20 +1088,52 @@ 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[5] = { +		{ "file ", tomoyo_write_file }, +		{ "network inet ", tomoyo_write_inet_network }, +		{ "network unix ", tomoyo_write_unix_network }, +		{ "misc ", tomoyo_write_misc }, +		{ "task ", tomoyo_write_task }, +	}; +	u8 i; + +	for (i = 0; i < ARRAY_SIZE(tomoyo_callback); 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.   * @@ -840,69 +1146,207 @@ 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 == '<') { +		int ret = 0;  		domain = NULL;  		if (is_delete) -			tomoyo_delete_domain(data); +			ret = tomoyo_delete_domain(data);  		else if (is_select)  			domain = tomoyo_find_domain(data);  		else -			domain = tomoyo_assign_domain(data, 0); -		head->write_var1 = domain; -		return 0; +			domain = tomoyo_assign_domain(data, false); +		head->w.domain = domain; +		return ret;  	}  	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++; +		if (cond->transit) { +			tomoyo_set_space(head); +			tomoyo_set_string(head, cond->transit->name); +		} +		/* 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: +		if (cond->grant_log != TOMOYO_GRANTLOG_AUTO) +			tomoyo_io_printf(head, " grant_log=%s", +					 tomoyo_yesno(cond->grant_log == +						      TOMOYO_GRANTLOG_YES)); +		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);  }  /** @@ -917,99 +1361,204 @@ 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 (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) { +		struct tomoyo_task_acl *ptr = +			container_of(acl, typeof(*ptr), head); +		tomoyo_set_group(head, "task "); +		tomoyo_set_string(head, "manual_domain_transition "); +		tomoyo_set_string(head, ptr->domainname->name); +	} 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);  		tomoyo_print_number_union(head, &ptr->minor); +	} else if (acl_type == TOMOYO_TYPE_INET_ACL) { +		struct tomoyo_inet_acl *ptr = +			container_of(acl, typeof(*ptr), head); +		const u8 perm = ptr->perm; + +		for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { +			if (!(perm & (1 << bit))) +				continue; +			if (first) { +				tomoyo_set_group(head, "network inet "); +				tomoyo_set_string(head, tomoyo_proto_keyword +						  [ptr->protocol]); +				tomoyo_set_space(head); +				first = false; +			} else { +				tomoyo_set_slash(head); +			} +			tomoyo_set_string(head, tomoyo_socket_keyword[bit]); +		} +		if (first) +			return true; +		tomoyo_set_space(head); +		if (ptr->address.group) { +			tomoyo_set_string(head, "@"); +			tomoyo_set_string(head, ptr->address.group->group_name +					  ->name); +		} else { +			char buf[128]; +			tomoyo_print_ip(buf, sizeof(buf), &ptr->address); +			tomoyo_io_printf(head, "%s", buf); +		} +		tomoyo_print_number_union(head, &ptr->port); +	} else if (acl_type == TOMOYO_TYPE_UNIX_ACL) { +		struct tomoyo_unix_acl *ptr = +			container_of(acl, typeof(*ptr), head); +		const u8 perm = ptr->perm; + +		for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { +			if (!(perm & (1 << bit))) +				continue; +			if (first) { +				tomoyo_set_group(head, "network unix "); +				tomoyo_set_string(head, tomoyo_proto_keyword +						  [ptr->protocol]); +				tomoyo_set_space(head); +				first = false; +			} else { +				tomoyo_set_slash(head); +			} +			tomoyo_set_string(head, tomoyo_socket_keyword[bit]); +		} +		if (first) +			return true; +		tomoyo_print_name_union(head, &ptr->name);  	} 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); +	} else if (acl_type == TOMOYO_TYPE_ENV_ACL) { +		struct tomoyo_env_acl *ptr = +			container_of(acl, typeof(*ptr), head); + +		tomoyo_set_group(head, "misc env "); +		tomoyo_set_string(head, ptr->env->name); +	} +	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);  	} -	head->r.bit = bit + 1; -	tomoyo_io_printf(head, "\n"); -	if (acl_type != TOMOYO_TYPE_MOUNT_ACL) -		goto next; - done: -	head->r.bit = 0;  	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)) @@ -1034,6 +1583,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) @@ -1041,22 +1591,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)) @@ -1073,73 +1619,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". @@ -1182,14 +1661,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)  		global_pid = true;  	pid = (unsigned int) simple_strtoul(buf, NULL, 10);  	rcu_read_lock(); -	read_lock(&tasklist_lock);  	if (global_pid)  		p = find_task_by_pid_ns(pid, &init_pid_ns);  	else  		p = find_task_by_vpid(pid);  	if (p)  		domain = tomoyo_real_domain(p); -	read_unlock(&tasklist_lock);  	rcu_read_unlock();  	if (!domain)  		return; @@ -1197,18 +1674,21 @@ 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 ", +	[TOMOYO_ADDRESS_GROUP] = "address_group ",  };  /** @@ -1222,34 +1702,35 @@ 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;  }  /** - * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. + * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.   *   * @head: Pointer to "struct tomoyo_io_buffer".   * @idx:  Index number. @@ -1260,9 +1741,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); @@ -1270,6 +1754,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) { @@ -1282,6 +1767,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)  							  (ptr,  						   struct tomoyo_number_group,  							   head)->number); +			} else if (idx == TOMOYO_ADDRESS_GROUP) { +				char buffer[128]; + +				struct tomoyo_address_group *member = +					container_of(ptr, typeof(*member), +						     head); +				tomoyo_print_ip(buffer, sizeof(buffer), +						&member->address); +				tomoyo_io_printf(head, " %s", buffer);  			}  			tomoyo_set_lf(head);  		} @@ -1303,7 +1797,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) @@ -1315,35 +1812,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); @@ -1351,24 +1836,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;  		} @@ -1387,6 +1854,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 && @@ -1399,95 +1868,41 @@ 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; +	struct tomoyo_domain_info *domain;  	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. @@ -1495,10 +1910,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 @@ -1508,88 +1995,80 @@ 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) -		goto out; -	entry->query = kzalloc(len, GFP_NOFS); -	if (!entry->query) +	entry.query_len = strlen(entry.query) + 1; +	if (!error) { +		tomoyo_add_entry(r->domain, entry.query);  		goto out; -	len = ksize(entry->query); +	} +	len = tomoyo_round2(entry.query_len); +	entry.domain = r->domain;  	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++; @@ -1598,22 +2077,39 @@ 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;  }  /** + * tomoyo_find_domain_by_qid - Get domain by query id. + * + * @serial: Query ID assigned by tomoyo_supervisor(). + * + * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. + */ +static struct tomoyo_domain_info *tomoyo_find_domain_by_qid +(unsigned int serial) +{ +	struct tomoyo_query *ptr; +	struct tomoyo_domain_info *domain = NULL; +	spin_lock(&tomoyo_query_list_lock); +	list_for_each_entry(ptr, &tomoyo_query_list, list) { +		if (ptr->serial != serial) +			continue; +		domain = ptr->domain; +		break; +	} +	spin_unlock(&tomoyo_query_list_lock); +	return domain; +} + +/**   * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query.   *   * @file: Pointer to "struct file". @@ -1623,28 +2119,13 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)   *   * Waits for access requests which violated policy in enforcing mode.   */ -static int tomoyo_poll_query(struct file *file, poll_table *wait) +static unsigned int tomoyo_poll_query(struct file *file, poll_table *wait)  { -	struct list_head *tmp; -	bool found = false; -	u8 i; -	for (i = 0; i < 2; i++) { -		spin_lock(&tomoyo_query_list_lock); -		list_for_each(tmp, &tomoyo_query_list) { -			struct tomoyo_query *ptr = -				list_entry(tmp, typeof(*ptr), list); -			if (ptr->answer) -				continue; -			found = true; -			break; -		} -		spin_unlock(&tomoyo_query_list_lock); -		if (found) -			return POLLIN | POLLRDNORM; -		if (i) -			break; -		poll_wait(file, &tomoyo_query_wait, wait); -	} +	if (!list_empty(&tomoyo_query_list)) +		return POLLIN | POLLRDNORM; +	poll_wait(file, &tomoyo_query_wait, wait); +	if (!list_empty(&tomoyo_query_list)) +		return POLLIN | POLLRDNORM;  	return 0;  } @@ -1656,8 +2137,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; @@ -1668,8 +2149,6 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)  	spin_lock(&tomoyo_query_list_lock);  	list_for_each(tmp, &tomoyo_query_list) {  		struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); -		if (ptr->answer) -			continue;  		if (pos++ != head->r.query_index)  			continue;  		len = ptr->query_len; @@ -1680,15 +2159,13 @@ 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;  	spin_lock(&tomoyo_query_list_lock);  	list_for_each(tmp, &tomoyo_query_list) {  		struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); -		if (ptr->answer) -			continue;  		if (pos++ != head->r.query_index)  			continue;  		/* @@ -1696,7 +2173,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); @@ -1735,8 +2213,10 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)  		struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);  		if (ptr->serial != serial)  			continue; -		if (!ptr->answer) -			ptr->answer = answer; +		ptr->answer = answer; +		/* Remove from tomoyo_query_list. */ +		if (ptr->answer) +			list_del_init(&ptr->list);  		break;  	}  	spin_unlock(&tomoyo_query_list_lock); @@ -1753,29 +2233,107 @@ 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.5.0");  		head->r.eof = true;  	}  } +/* 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_self_domain - Get the current process's domainname. + * tomoyo_read_stat - Read statistic data.   *   * @head: Pointer to "struct tomoyo_io_buffer".   * - * Returns the current process's domainname. + * Returns nothing.   */ -static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) +static void tomoyo_read_stat(struct tomoyo_io_buffer *head)  { -	if (!head->r.eof) { -		/* -		 * tomoyo_domain()->domainname != NULL -		 * because every process belongs to a domain and -		 * the domain's name cannot be NULL. -		 */ -		tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); -		head->r.eof = true; +	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;  }  /** @@ -1784,9 +2342,7 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)   * @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)  { @@ -1807,14 +2363,10 @@ int tomoyo_open_control(const u8 type, struct file *file)  		head->write = tomoyo_write_exception;  		head->read = tomoyo_read_exception;  		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; +	case TOMOYO_AUDIT: +		/* /sys/kernel/security/tomoyo/audit */ +		head->poll = tomoyo_poll_log; +		head->read = tomoyo_read_log;  		break;  	case TOMOYO_PROCESS_STATUS:  		/* /sys/kernel/security/tomoyo/.process_status */ @@ -1826,11 +2378,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 */ @@ -1880,26 +2432,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;  } @@ -1907,35 +2449,72 @@ int tomoyo_open_control(const u8 type, struct file *file)   * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface.   *   * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". + * @wait: Pointer to "poll_table". Maybe NULL.   * - * Waits for read readiness. - * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, + * POLLOUT | POLLWRNORM otherwise.   */ -int tomoyo_poll_control(struct file *file, poll_table *wait) +unsigned int tomoyo_poll_control(struct file *file, poll_table *wait)  {  	struct tomoyo_io_buffer *head = file->private_data; -	if (!head->poll) -		return -ENOSYS; -	return head->poll(file, wait); +	if (head->poll) +		return head->poll(file, wait) | POLLOUT | POLLWRNORM; +	return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; +} + +/** + * 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; @@ -1943,64 +2522,157 @@ 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; +	head->read_user_buf_avail = 0; +	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;  } @@ -2008,36 +2680,18 @@ 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. - * - * Caller looses tomoyo_read_lock(). + * @head: Pointer to "struct tomoyo_io_buffer".   */ -int tomoyo_close_control(struct file *file) +void 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(); -	return 0; +	if (head->type == TOMOYO_QUERY && +	    atomic_dec_and_test(&tomoyo_query_observers)) +		wake_up_all(&tomoyo_answer_wait); +	tomoyo_notify_gc(head, false);  }  /** @@ -2048,27 +2702,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.5.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 != 20110903) +			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.5 must be installed and " +		       "policy must be initialized.\n"); +		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "  		       "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 +} diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 7c66bd89878..b897d486201 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -3,7 +3,7 @@   *   * Header file for TOMOYO.   * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #ifndef _SECURITY_TOMOYO_COMMON_H @@ -21,7 +21,18 @@  #include <linux/list.h>  #include <linux/cred.h>  #include <linux/poll.h> -struct linux_binprm; +#include <linux/binfmts.h> +#include <linux/highmem.h> +#include <linux/net.h> +#include <linux/inet.h> +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/un.h> +#include <net/sock.h> +#include <net/af_unix.h> +#include <net/ip.h> +#include <net/ipv6.h> +#include <net/udp.h>  /********** Constants definitions. **********/ @@ -33,71 +44,175 @@ struct linux_binprm;  #define TOMOYO_HASH_BITS  8  #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) +/* + * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. + * Therefore, we don't need SOCK_MAX. + */ +#define TOMOYO_SOCK_MAX 6 +  #define TOMOYO_EXEC_TMPSIZE     4096 +/* Garbage collector is trying to kfree() this element. */ +#define TOMOYO_GC_IN_PROGRESS -1 +  /* Profile number is an integer between 0 and 255. */  #define TOMOYO_MAX_PROFILES 256 +/* Group number is an integer between 0 and 255. */ +#define TOMOYO_MAX_ACL_GROUPS 256 + +/* Index numbers for "struct tomoyo_condition". */ +enum tomoyo_conditions_index { +	TOMOYO_TASK_UID,             /* current_uid()   */ +	TOMOYO_TASK_EUID,            /* current_euid()  */ +	TOMOYO_TASK_SUID,            /* current_suid()  */ +	TOMOYO_TASK_FSUID,           /* current_fsuid() */ +	TOMOYO_TASK_GID,             /* current_gid()   */ +	TOMOYO_TASK_EGID,            /* current_egid()  */ +	TOMOYO_TASK_SGID,            /* current_sgid()  */ +	TOMOYO_TASK_FSGID,           /* current_fsgid() */ +	TOMOYO_TASK_PID,             /* sys_getpid()   */ +	TOMOYO_TASK_PPID,            /* sys_getppid()  */ +	TOMOYO_EXEC_ARGC,            /* "struct linux_binprm *"->argc */ +	TOMOYO_EXEC_ENVC,            /* "struct linux_binprm *"->envc */ +	TOMOYO_TYPE_IS_SOCKET,       /* S_IFSOCK */ +	TOMOYO_TYPE_IS_SYMLINK,      /* S_IFLNK */ +	TOMOYO_TYPE_IS_FILE,         /* S_IFREG */ +	TOMOYO_TYPE_IS_BLOCK_DEV,    /* S_IFBLK */ +	TOMOYO_TYPE_IS_DIRECTORY,    /* S_IFDIR */ +	TOMOYO_TYPE_IS_CHAR_DEV,     /* S_IFCHR */ +	TOMOYO_TYPE_IS_FIFO,         /* S_IFIFO */ +	TOMOYO_MODE_SETUID,          /* S_ISUID */ +	TOMOYO_MODE_SETGID,          /* S_ISGID */ +	TOMOYO_MODE_STICKY,          /* S_ISVTX */ +	TOMOYO_MODE_OWNER_READ,      /* S_IRUSR */ +	TOMOYO_MODE_OWNER_WRITE,     /* S_IWUSR */ +	TOMOYO_MODE_OWNER_EXECUTE,   /* S_IXUSR */ +	TOMOYO_MODE_GROUP_READ,      /* S_IRGRP */ +	TOMOYO_MODE_GROUP_WRITE,     /* S_IWGRP */ +	TOMOYO_MODE_GROUP_EXECUTE,   /* S_IXGRP */ +	TOMOYO_MODE_OTHERS_READ,     /* S_IROTH */ +	TOMOYO_MODE_OTHERS_WRITE,    /* S_IWOTH */ +	TOMOYO_MODE_OTHERS_EXECUTE,  /* S_IXOTH */ +	TOMOYO_EXEC_REALPATH, +	TOMOYO_SYMLINK_TARGET, +	TOMOYO_PATH1_UID, +	TOMOYO_PATH1_GID, +	TOMOYO_PATH1_INO, +	TOMOYO_PATH1_MAJOR, +	TOMOYO_PATH1_MINOR, +	TOMOYO_PATH1_PERM, +	TOMOYO_PATH1_TYPE, +	TOMOYO_PATH1_DEV_MAJOR, +	TOMOYO_PATH1_DEV_MINOR, +	TOMOYO_PATH2_UID, +	TOMOYO_PATH2_GID, +	TOMOYO_PATH2_INO, +	TOMOYO_PATH2_MAJOR, +	TOMOYO_PATH2_MINOR, +	TOMOYO_PATH2_PERM, +	TOMOYO_PATH2_TYPE, +	TOMOYO_PATH2_DEV_MAJOR, +	TOMOYO_PATH2_DEV_MINOR, +	TOMOYO_PATH1_PARENT_UID, +	TOMOYO_PATH1_PARENT_GID, +	TOMOYO_PATH1_PARENT_INO, +	TOMOYO_PATH1_PARENT_PERM, +	TOMOYO_PATH2_PARENT_UID, +	TOMOYO_PATH2_PARENT_GID, +	TOMOYO_PATH2_PARENT_INO, +	TOMOYO_PATH2_PARENT_PERM, +	TOMOYO_MAX_CONDITION_KEYWORD, +	TOMOYO_NUMBER_UNION, +	TOMOYO_NAME_UNION, +	TOMOYO_ARGV_ENTRY, +	TOMOYO_ENVP_ENTRY, +}; + + +/* Index numbers for stat(). */ +enum tomoyo_path_stat_index { +	/* Do not change this order. */ +	TOMOYO_PATH1, +	TOMOYO_PATH1_PARENT, +	TOMOYO_PATH2, +	TOMOYO_PATH2_PARENT, +	TOMOYO_MAX_PATH_STAT +}; + +/* Index numbers for operation mode. */  enum tomoyo_mode_index {  	TOMOYO_CONFIG_DISABLED,  	TOMOYO_CONFIG_LEARNING,  	TOMOYO_CONFIG_PERMISSIVE,  	TOMOYO_CONFIG_ENFORCING, -	TOMOYO_CONFIG_USE_DEFAULT = 255 +	TOMOYO_CONFIG_MAX_MODE, +	TOMOYO_CONFIG_WANT_REJECT_LOG =  64, +	TOMOYO_CONFIG_WANT_GRANT_LOG  = 128, +	TOMOYO_CONFIG_USE_DEFAULT     = 255,  }; +/* Index numbers for entry type. */  enum tomoyo_policy_id {  	TOMOYO_ID_GROUP, +	TOMOYO_ID_ADDRESS_GROUP,  	TOMOYO_ID_PATH_GROUP,  	TOMOYO_ID_NUMBER_GROUP,  	TOMOYO_ID_TRANSITION_CONTROL,  	TOMOYO_ID_AGGREGATOR, -	TOMOYO_ID_GLOBALLY_READABLE, -	TOMOYO_ID_PATTERN, -	TOMOYO_ID_NO_REWRITE,  	TOMOYO_ID_MANAGER, +	TOMOYO_ID_CONDITION,  	TOMOYO_ID_NAME,  	TOMOYO_ID_ACL,  	TOMOYO_ID_DOMAIN,  	TOMOYO_MAX_POLICY  }; +/* Index numbers for domain's attributes. */ +enum tomoyo_domain_info_flags_index { +	/* Quota warnning flag.   */ +	TOMOYO_DIF_QUOTA_WARNED, +	/* +	 * This domain was unable to create a new domain at +	 * tomoyo_find_next_domain() because the name of the domain to be +	 * created was too long or it could not allocate memory. +	 * More than one process continued execve() without domain transition. +	 */ +	TOMOYO_DIF_TRANSITION_FAILED, +	TOMOYO_MAX_DOMAIN_INFO_FLAGS +}; + +/* Index numbers for audit type. */ +enum tomoyo_grant_log { +	/* Follow profile's configuration. */ +	TOMOYO_GRANTLOG_AUTO, +	/* Do not generate grant log. */ +	TOMOYO_GRANTLOG_NO, +	/* Generate grant_log. */ +	TOMOYO_GRANTLOG_YES, +}; + +/* Index numbers for group entries. */  enum tomoyo_group_id {  	TOMOYO_PATH_GROUP,  	TOMOYO_NUMBER_GROUP, +	TOMOYO_ADDRESS_GROUP,  	TOMOYO_MAX_GROUP  }; -/* Keywords for ACLs. */ -#define TOMOYO_KEYWORD_AGGREGATOR                "aggregator " -#define TOMOYO_KEYWORD_ALLOW_MOUNT               "allow_mount " -#define TOMOYO_KEYWORD_ALLOW_READ                "allow_read " -#define TOMOYO_KEYWORD_DELETE                    "delete " -#define TOMOYO_KEYWORD_DENY_REWRITE              "deny_rewrite " -#define TOMOYO_KEYWORD_FILE_PATTERN              "file_pattern " -#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN         "initialize_domain " -#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain " -#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain " -#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain " -#define TOMOYO_KEYWORD_PATH_GROUP                "path_group " -#define TOMOYO_KEYWORD_NUMBER_GROUP              "number_group " -#define TOMOYO_KEYWORD_SELECT                    "select " -#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile " -#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read" -#define TOMOYO_KEYWORD_QUOTA_EXCEEDED            "quota_exceeded" -#define TOMOYO_KEYWORD_TRANSITION_FAILED         "transition_failed" -/* A domain definition starts with <kernel>. */ -#define TOMOYO_ROOT_NAME                         "<kernel>" -#define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1) - -/* Value type definition. */ -#define TOMOYO_VALUE_TYPE_INVALID     0 -#define TOMOYO_VALUE_TYPE_DECIMAL     1 -#define TOMOYO_VALUE_TYPE_OCTAL       2 -#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 +/* Index numbers for type of numeric values. */ +enum tomoyo_value_type { +	TOMOYO_VALUE_TYPE_INVALID, +	TOMOYO_VALUE_TYPE_DECIMAL, +	TOMOYO_VALUE_TYPE_OCTAL, +	TOMOYO_VALUE_TYPE_HEXADECIMAL, +}; +/* Index numbers for domain transition control keywords. */  enum tomoyo_transition_type {  	/* Do not change this order, */ +	TOMOYO_TRANSITION_CONTROL_NO_RESET, +	TOMOYO_TRANSITION_CONTROL_RESET,  	TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE,  	TOMOYO_TRANSITION_CONTROL_INITIALIZE,  	TOMOYO_TRANSITION_CONTROL_NO_KEEP, @@ -112,37 +227,35 @@ enum tomoyo_acl_entry_type_index {  	TOMOYO_TYPE_PATH_NUMBER_ACL,  	TOMOYO_TYPE_MKDEV_ACL,  	TOMOYO_TYPE_MOUNT_ACL, +	TOMOYO_TYPE_INET_ACL, +	TOMOYO_TYPE_UNIX_ACL, +	TOMOYO_TYPE_ENV_ACL, +	TOMOYO_TYPE_MANUAL_TASK_ACL,  }; -/* Index numbers for File Controls. */ - -/* - * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically - * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. - * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if - * TOMOYO_TYPE_READ_WRITE is set. - * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ - * or TOMOYO_TYPE_WRITE is cleared. - * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if - * TOMOYO_TYPE_READ_WRITE is cleared. - */ - +/* Index numbers for access controls with one pathname. */  enum tomoyo_path_acl_index { -	TOMOYO_TYPE_READ_WRITE,  	TOMOYO_TYPE_EXECUTE,  	TOMOYO_TYPE_READ,  	TOMOYO_TYPE_WRITE, +	TOMOYO_TYPE_APPEND,  	TOMOYO_TYPE_UNLINK, +	TOMOYO_TYPE_GETATTR,  	TOMOYO_TYPE_RMDIR,  	TOMOYO_TYPE_TRUNCATE,  	TOMOYO_TYPE_SYMLINK, -	TOMOYO_TYPE_REWRITE,  	TOMOYO_TYPE_CHROOT,  	TOMOYO_TYPE_UMOUNT,  	TOMOYO_MAX_PATH_OPERATION  }; -#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE)) +/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */ +enum tomoyo_memory_stat_type { +	TOMOYO_MEMORY_POLICY, +	TOMOYO_MEMORY_AUDIT, +	TOMOYO_MEMORY_QUERY, +	TOMOYO_MAX_MEMORY_STAT +};  enum tomoyo_mkdev_acl_index {  	TOMOYO_TYPE_MKBLOCK, @@ -150,6 +263,16 @@ enum tomoyo_mkdev_acl_index {  	TOMOYO_MAX_MKDEV_OPERATION  }; +/* Index numbers for socket operations. */ +enum tomoyo_network_acl_index { +	TOMOYO_NETWORK_BIND,    /* bind() operation. */ +	TOMOYO_NETWORK_LISTEN,  /* listen() operation. */ +	TOMOYO_NETWORK_CONNECT, /* connect() operation. */ +	TOMOYO_NETWORK_SEND,    /* send() operation. */ +	TOMOYO_MAX_NETWORK_OPERATION +}; + +/* Index numbers for access controls with two pathnames. */  enum tomoyo_path2_acl_index {  	TOMOYO_TYPE_LINK,  	TOMOYO_TYPE_RENAME, @@ -157,6 +280,7 @@ enum tomoyo_path2_acl_index {  	TOMOYO_MAX_PATH2_OPERATION  }; +/* Index numbers for access controls with one pathname and one number. */  enum tomoyo_path_number_acl_index {  	TOMOYO_TYPE_CREATE,  	TOMOYO_TYPE_MKDIR, @@ -169,31 +293,44 @@ enum tomoyo_path_number_acl_index {  	TOMOYO_MAX_PATH_NUMBER_OPERATION  }; +/* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */  enum tomoyo_securityfs_interface_index {  	TOMOYO_DOMAINPOLICY,  	TOMOYO_EXCEPTIONPOLICY, -	TOMOYO_DOMAIN_STATUS,  	TOMOYO_PROCESS_STATUS, -	TOMOYO_MEMINFO, -	TOMOYO_SELFDOMAIN, +	TOMOYO_STAT, +	TOMOYO_AUDIT,  	TOMOYO_VERSION,  	TOMOYO_PROFILE,  	TOMOYO_QUERY,  	TOMOYO_MANAGER  }; +/* Index numbers for special mount operations. */ +enum tomoyo_special_mount { +	TOMOYO_MOUNT_BIND,            /* mount --bind /source /dest   */ +	TOMOYO_MOUNT_MOVE,            /* mount --move /old /new       */ +	TOMOYO_MOUNT_REMOUNT,         /* mount -o remount /dir        */ +	TOMOYO_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ +	TOMOYO_MOUNT_MAKE_PRIVATE,    /* mount --make-private /dir    */ +	TOMOYO_MOUNT_MAKE_SLAVE,      /* mount --make-slave /dir      */ +	TOMOYO_MOUNT_MAKE_SHARED,     /* mount --make-shared /dir     */ +	TOMOYO_MAX_SPECIAL_MOUNT +}; + +/* Index numbers for functionality. */  enum tomoyo_mac_index {  	TOMOYO_MAC_FILE_EXECUTE,  	TOMOYO_MAC_FILE_OPEN,  	TOMOYO_MAC_FILE_CREATE,  	TOMOYO_MAC_FILE_UNLINK, +	TOMOYO_MAC_FILE_GETATTR,  	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, @@ -206,41 +343,87 @@ enum tomoyo_mac_index {  	TOMOYO_MAC_FILE_MOUNT,  	TOMOYO_MAC_FILE_UMOUNT,  	TOMOYO_MAC_FILE_PIVOT_ROOT, +	TOMOYO_MAC_NETWORK_INET_STREAM_BIND, +	TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN, +	TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT, +	TOMOYO_MAC_NETWORK_INET_DGRAM_BIND, +	TOMOYO_MAC_NETWORK_INET_DGRAM_SEND, +	TOMOYO_MAC_NETWORK_INET_RAW_BIND, +	TOMOYO_MAC_NETWORK_INET_RAW_SEND, +	TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND, +	TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN, +	TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT, +	TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND, +	TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND, +	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND, +	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, +	TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, +	TOMOYO_MAC_ENVIRON,  	TOMOYO_MAX_MAC_INDEX  }; +/* Index numbers for category of functionality. */  enum tomoyo_mac_category_index {  	TOMOYO_MAC_CATEGORY_FILE, +	TOMOYO_MAC_CATEGORY_NETWORK, +	TOMOYO_MAC_CATEGORY_MISC,  	TOMOYO_MAX_MAC_CATEGORY_INDEX  }; -#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ - -/********** Structure definitions. **********/ -  /* - * tomoyo_acl_head is a structure which is used for holding elements not in - * domain policy. - * It has following fields. + * Retry this request. Returned by tomoyo_supervisor() if policy violation has + * occurred in enforcing mode and the userspace daemon decided to retry.   * - *  (1) "list" which is linked to tomoyo_policy_list[] . - *  (2) "is_deleted" is a bool which is true if marked as deleted, false - *      otherwise. + * We must choose a positive value in order to distinguish "granted" (which is + * 0) and "rejected" (which is a negative value) and "retry".   */ +#define TOMOYO_RETRY_REQUEST 1 + +/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */ +enum tomoyo_policy_stat_type { +	/* Do not change this order. */ +	TOMOYO_STAT_POLICY_UPDATES, +	TOMOYO_STAT_POLICY_LEARNING,   /* == TOMOYO_CONFIG_LEARNING */ +	TOMOYO_STAT_POLICY_PERMISSIVE, /* == TOMOYO_CONFIG_PERMISSIVE */ +	TOMOYO_STAT_POLICY_ENFORCING,  /* == TOMOYO_CONFIG_ENFORCING */ +	TOMOYO_MAX_POLICY_STAT +}; + +/* Index numbers for profile's PREFERENCE values. */ +enum tomoyo_pref_index { +	TOMOYO_PREF_MAX_AUDIT_LOG, +	TOMOYO_PREF_MAX_LEARNING_ENTRY, +	TOMOYO_MAX_PREF +}; + +/********** Structure definitions. **********/ + +/* Common header for holding ACL entries. */  struct tomoyo_acl_head {  	struct list_head list; -	bool is_deleted; +	s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */  } __packed; -/* - * tomoyo_request_info is a structure which is used for holding - * - * (1) Domain information of current process. - * (2) How many retries are made for this request. - * (3) Profile number used for this request. - * (4) Access control mode of the profile. - */ +/* Common header for shared entries. */ +struct tomoyo_shared_acl_head { +	struct list_head list; +	atomic_t users; +} __packed; + +struct tomoyo_policy_namespace; + +/* Structure for request info. */  struct tomoyo_request_info { +	/* +	 * For holding parameters specific to operations which deal files. +	 * NULL if not dealing files. +	 */ +	struct tomoyo_obj_info *obj; +	/* +	 * For holding parameters specific to execve() request. +	 * NULL if not dealing do_execve(). +	 */ +	struct tomoyo_execve *ee;  	struct tomoyo_domain_info *domain;  	/* For holding parameters. */  	union { @@ -248,11 +431,13 @@ struct tomoyo_request_info {  			const struct tomoyo_path_info *filename;  			/* For using wildcards at tomoyo_find_next_domain(). */  			const struct tomoyo_path_info *matched_path; +			/* One of values in "enum tomoyo_path_acl_index". */  			u8 operation;  		} path;  		struct {  			const struct tomoyo_path_info *filename1;  			const struct tomoyo_path_info *filename2; +			/* One of values in "enum tomoyo_path2_acl_index". */  			u8 operation;  		} path2;  		struct { @@ -260,21 +445,49 @@ struct tomoyo_request_info {  			unsigned int mode;  			unsigned int major;  			unsigned int minor; +			/* One of values in "enum tomoyo_mkdev_acl_index". */  			u8 operation;  		} mkdev;  		struct {  			const struct tomoyo_path_info *filename;  			unsigned long number; +			/* +			 * One of values in +			 * "enum tomoyo_path_number_acl_index". +			 */  			u8 operation;  		} path_number;  		struct { +			const struct tomoyo_path_info *name; +		} environ; +		struct { +			const __be32 *address; +			u16 port; +			/* One of values smaller than TOMOYO_SOCK_MAX. */ +			u8 protocol; +			/* One of values in "enum tomoyo_network_acl_index". */ +			u8 operation; +			bool is_ipv6; +		} inet_network; +		struct { +			const struct tomoyo_path_info *address; +			/* One of values smaller than TOMOYO_SOCK_MAX. */ +			u8 protocol; +			/* One of values in "enum tomoyo_network_acl_index". */ +			u8 operation; +		} unix_network; +		struct {  			const struct tomoyo_path_info *type;  			const struct tomoyo_path_info *dir;  			const struct tomoyo_path_info *dev;  			unsigned long flags;  			int need_dev;  		} mount; +		struct { +			const struct tomoyo_path_info *domainname; +		} task;  	} param; +	struct tomoyo_acl_info *matched_acl;  	u8 param_type;  	bool granted;  	u8 retry; @@ -283,26 +496,7 @@ struct tomoyo_request_info {  	u8 type;  }; -/* - * tomoyo_path_info is a structure which is used for holding a string data - * used by TOMOYO. - * This structure has several fields for supporting pattern matching. - * - * (1) "name" is the '\0' terminated string data. - * (2) "hash" is full_name_hash(name, strlen(name)). - *     This allows tomoyo_pathcmp() to compare by hash before actually compare - *     using strcmp(). - * (3) "const_len" is the length of the initial segment of "name" which - *     consists entirely of non wildcard characters. In other words, the length - *     which we can compare two strings using strncmp(). - * (4) "is_dir" is a bool which is true if "name" ends with "/", - *     false otherwise. - *     TOMOYO distinguishes directory and non-directory. A directory ends with - *     "/" and non-directory does not end with "/". - * (5) "is_patterned" is a bool which is true if "name" contains wildcard - *     characters, false otherwise. This allows TOMOYO to use "hash" and - *     strcmp() for string comparison if "is_patterned" is false. - */ +/* Structure for holding a token. */  struct tomoyo_path_info {  	const char *name;  	u32 hash;          /* = full_name_hash(name, strlen(name)) */ @@ -311,36 +505,39 @@ struct tomoyo_path_info {  	bool is_patterned; /* = tomoyo_path_contains_pattern(name) */  }; -/* - * tomoyo_name is a structure which is used for linking - * "struct tomoyo_path_info" into tomoyo_name_list . - */ +/* Structure for holding string data. */  struct tomoyo_name { -	struct list_head list; -	atomic_t users; +	struct tomoyo_shared_acl_head head;  	struct tomoyo_path_info entry;  }; +/* Structure for holding a word. */  struct tomoyo_name_union { +	/* Either @filename or @group is NULL. */  	const struct tomoyo_path_info *filename;  	struct tomoyo_group *group; -	u8 is_group;  }; +/* Structure for holding a number. */  struct tomoyo_number_union {  	unsigned long values[2]; -	struct tomoyo_group *group; -	u8 min_type; -	u8 max_type; -	u8 is_group; +	struct tomoyo_group *group; /* Maybe NULL. */ +	/* One of values in "enum tomoyo_value_type". */ +	u8 value_type[2]; +}; + +/* Structure for holding an IP address. */ +struct tomoyo_ipaddr_union { +	struct in6_addr ip[2]; /* Big endian. */ +	struct tomoyo_group *group; /* Pointer to address group. */ +	bool is_ipv6; /* Valid only if @group == NULL. */  }; -/* Structure for "path_group"/"number_group" directive. */ +/* Structure for "path_group"/"number_group"/"address_group" directive. */  struct tomoyo_group { -	struct list_head list; +	struct tomoyo_shared_acl_head head;  	const struct tomoyo_path_info *group_name;  	struct list_head member_list; -	atomic_t users;  };  /* Structure for "path_group" directive. */ @@ -355,130 +552,177 @@ struct tomoyo_number_group {  	struct tomoyo_number_union number;  }; -/* - * tomoyo_acl_info is a structure which is used for holding - * - *  (1) "list" which is linked to the ->acl_info_list of - *      "struct tomoyo_domain_info" - *  (2) "is_deleted" is a bool which is true if this domain is marked as - *      "deleted", false otherwise. - *  (3) "type" which tells type of the entry. - * - * Packing "struct tomoyo_acl_info" allows - * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl" - * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed - * "u8" without enlarging their structure size. - */ +/* Structure for "address_group" directive. */ +struct tomoyo_address_group { +	struct tomoyo_acl_head head; +	/* Structure for holding an IP address. */ +	struct tomoyo_ipaddr_union address; +}; + +/* Subset of "struct stat". Used by conditional ACL and audit logs. */ +struct tomoyo_mini_stat { +	kuid_t uid; +	kgid_t gid; +	ino_t ino; +	umode_t mode; +	dev_t dev; +	dev_t rdev; +}; + +/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */ +struct tomoyo_page_dump { +	struct page *page;    /* Previously dumped page. */ +	char *data;           /* Contents of "page". Size is PAGE_SIZE. */ +}; + +/* Structure for attribute checks in addition to pathname checks. */ +struct tomoyo_obj_info { +	/* +	 * True if tomoyo_get_attributes() was already called, false otherwise. +	 */ +	bool validate_done; +	/* True if @stat[] is valid. */ +	bool stat_valid[TOMOYO_MAX_PATH_STAT]; +	/* First pathname. Initialized with { NULL, NULL } if no path. */ +	struct path path1; +	/* Second pathname. Initialized with { NULL, NULL } if no path. */ +	struct path path2; +	/* +	 * Information on @path1, @path1's parent directory, @path2, @path2's +	 * parent directory. +	 */ +	struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT]; +	/* +	 * Content of symbolic link to be created. NULL for operations other +	 * than symlink(). +	 */ +	struct tomoyo_path_info *symlink_target; +}; + +/* Structure for argv[]. */ +struct tomoyo_argv { +	unsigned long index; +	const struct tomoyo_path_info *value; +	bool is_not; +}; + +/* Structure for envp[]. */ +struct tomoyo_envp { +	const struct tomoyo_path_info *name; +	const struct tomoyo_path_info *value; +	bool is_not; +}; + +/* Structure for execve() operation. */ +struct tomoyo_execve { +	struct tomoyo_request_info r; +	struct tomoyo_obj_info obj; +	struct linux_binprm *bprm; +	const struct tomoyo_path_info *transition; +	/* For dumping argv[] and envp[]. */ +	struct tomoyo_page_dump dump; +	/* For temporary use. */ +	char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */ +}; + +/* Structure for entries which follows "struct tomoyo_condition". */ +struct tomoyo_condition_element { +	/* +	 * Left hand operand. A "struct tomoyo_argv" for TOMOYO_ARGV_ENTRY, a +	 * "struct tomoyo_envp" for TOMOYO_ENVP_ENTRY is attached to the tail +	 * of the array of this struct. +	 */ +	u8 left; +	/* +	 * Right hand operand. A "struct tomoyo_number_union" for +	 * TOMOYO_NUMBER_UNION, a "struct tomoyo_name_union" for +	 * TOMOYO_NAME_UNION is attached to the tail of the array of this +	 * struct. +	 */ +	u8 right; +	/* Equation operator. True if equals or overlaps, false otherwise. */ +	bool equals; +}; + +/* Structure for optional arguments. */ +struct tomoyo_condition { +	struct tomoyo_shared_acl_head head; +	u32 size; /* Memory size allocated for this entry. */ +	u16 condc; /* Number of conditions in this struct. */ +	u16 numbers_count; /* Number of "struct tomoyo_number_union values". */ +	u16 names_count; /* Number of "struct tomoyo_name_union names". */ +	u16 argc; /* Number of "struct tomoyo_argv". */ +	u16 envc; /* Number of "struct tomoyo_envp". */ +	u8 grant_log; /* One of values in "enum tomoyo_grant_log". */ +	const struct tomoyo_path_info *transit; /* Maybe NULL. */ +	/* +	 * struct tomoyo_condition_element condition[condc]; +	 * struct tomoyo_number_union values[numbers_count]; +	 * struct tomoyo_name_union names[names_count]; +	 * struct tomoyo_argv argv[argc]; +	 * struct tomoyo_envp envp[envc]; +	 */ +}; + +/* Common header for individual entries. */  struct tomoyo_acl_info {  	struct list_head list; -	bool is_deleted; -	u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */ +	struct tomoyo_condition *cond; /* Maybe NULL. */ +	s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ +	u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */  } __packed; -/* - * tomoyo_domain_info is a structure which is used for holding permissions - * (e.g. "allow_read /lib/libc-2.5.so") given to each domain. - * It has following fields. - * - *  (1) "list" which is linked to tomoyo_domain_list . - *  (2) "acl_info_list" which is linked to "struct tomoyo_acl_info". - *  (3) "domainname" which holds the name of the domain. - *  (4) "profile" which remembers profile number assigned to this domain. - *  (5) "is_deleted" is a bool which is true if this domain is marked as - *      "deleted", false otherwise. - *  (6) "quota_warned" is a bool which is used for suppressing warning message - *      when learning mode learned too much entries. - *  (7) "ignore_global_allow_read" is a bool which is true if this domain - *      should ignore "allow_read" directive in exception policy. - *  (8) "transition_failed" is a bool which is set to true when this domain was - *      unable to create a new domain at tomoyo_find_next_domain() because the - *      name of the domain to be created was too long or it could not allocate - *      memory. If set to true, more than one process continued execve() - *      without domain transition. - *  (9) "users" is an atomic_t that holds how many "struct cred"->security - *      are referring this "struct tomoyo_domain_info". If is_deleted == true - *      and users == 0, this struct will be kfree()d upon next garbage - *      collection. - * - * A domain's lifecycle is an analogy of files on / directory. - * Multiple domains with the same domainname cannot be created (as with - * creating files with the same filename fails with -EEXIST). - * If a process reached a domain, that process can reside in that domain after - * that domain is marked as "deleted" (as with a process can access an already - * open()ed file after that file was unlink()ed). - */ +/* Structure for domain information. */  struct tomoyo_domain_info {  	struct list_head list;  	struct list_head acl_info_list;  	/* Name of this domain. Never NULL.          */  	const struct tomoyo_path_info *domainname; +	/* Namespace for this domain. Never NULL. */ +	struct tomoyo_policy_namespace *ns;  	u8 profile;        /* Profile number to use. */ +	u8 group;          /* Group number to use.   */  	bool is_deleted;   /* Delete flag.           */ -	bool quota_warned; /* Quota warnning flag.   */ -	bool ignore_global_allow_read; /* Ignore "allow_read" flag. */ -	bool transition_failed; /* Domain transition failed flag. */ +	bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];  	atomic_t users; /* Number of referring credentials. */  };  /* - * tomoyo_path_acl is a structure which is used for holding an - * entry with one pathname operation (e.g. open(), mkdir()). - * It has following fields. - * - *  (1) "head" which is a "struct tomoyo_acl_info". - *  (2) "perm" which is a bitmask of permitted operations. - *  (3) "name" is the pathname. - * - * Directives held by this structure are "allow_read/write", "allow_execute", - * "allow_read", "allow_write", "allow_unlink", "allow_rmdir", - * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and - * "allow_unmount". + * Structure for "task manual_domain_transition" directive. + */ +struct tomoyo_task_acl { +	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */ +	/* Pointer to domainname. */ +	const struct tomoyo_path_info *domainname; +}; + +/* + * Structure for "file execute", "file read", "file write", "file append", + * "file unlink", "file getattr", "file rmdir", "file truncate", + * "file symlink", "file chroot" and "file unmount" directive.   */  struct tomoyo_path_acl {  	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ -	u16 perm; +	u16 perm; /* Bitmask of values in "enum tomoyo_path_acl_index". */  	struct tomoyo_name_union name;  };  /* - * tomoyo_path_number_acl is a structure which is used for holding an - * entry with one pathname and one number operation. - * It has following fields. - * - *  (1) "head" which is a "struct tomoyo_acl_info". - *  (2) "perm" which is a bitmask of permitted operations. - *  (3) "name" is the pathname. - *  (4) "number" is the numeric value. - * - * Directives held by this structure are "allow_create", "allow_mkdir", - * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown" - * and "allow_chgrp". - * + * Structure for "file create", "file mkdir", "file mkfifo", "file mksock", + * "file ioctl", "file chmod", "file chown" and "file chgrp" directive.   */  struct tomoyo_path_number_acl {  	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */ +	/* Bitmask of values in "enum tomoyo_path_number_acl_index". */  	u8 perm;  	struct tomoyo_name_union name;  	struct tomoyo_number_union number;  }; -/* - * tomoyo_mkdev_acl is a structure which is used for holding an - * entry with one pathname and three numbers operation. - * It has following fields. - * - *  (1) "head" which is a "struct tomoyo_acl_info". - *  (2) "perm" which is a bitmask of permitted operations. - *  (3) "mode" is the create mode. - *  (4) "major" is the major number of device node. - *  (5) "minor" is the minor number of device node. - * - * Directives held by this structure are "allow_mkchar", "allow_mkblock". - * - */ +/* Structure for "file mkblock" and "file mkchar" directive. */  struct tomoyo_mkdev_acl {  	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */ -	u8 perm; +	u8 perm; /* Bitmask of values in "enum tomoyo_mkdev_acl_index". */  	struct tomoyo_name_union name;  	struct tomoyo_number_union mode;  	struct tomoyo_number_union major; @@ -486,38 +730,16 @@ struct tomoyo_mkdev_acl {  };  /* - * tomoyo_path2_acl is a structure which is used for holding an - * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). - * It has following fields. - * - *  (1) "head" which is a "struct tomoyo_acl_info". - *  (2) "perm" which is a bitmask of permitted operations. - *  (3) "name1" is the source/old pathname. - *  (4) "name2" is the destination/new pathname. - * - * Directives held by this structure are "allow_rename", "allow_link" and - * "allow_pivot_root". + * Structure for "file rename", "file link" and "file pivot_root" directive.   */  struct tomoyo_path2_acl {  	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */ -	u8 perm; +	u8 perm; /* Bitmask of values in "enum tomoyo_path2_acl_index". */  	struct tomoyo_name_union name1;  	struct tomoyo_name_union name2;  }; -/* - * tomoyo_mount_acl is a structure which is used for holding an - * entry for mount operation. - * It has following fields. - * - *  (1) "head" which is a "struct tomoyo_acl_info". - *  (2) "dev_name" is the device name. - *  (3) "dir_name" is the mount point. - *  (4) "fs_type" is the filesystem type. - *  (5) "flags" is the mount flags. - * - * Directive held by this structure is "allow_mount". - */ +/* Structure for "file mount" directive. */  struct tomoyo_mount_acl {  	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */  	struct tomoyo_name_union dev_name; @@ -526,7 +748,38 @@ struct tomoyo_mount_acl {  	struct tomoyo_number_union flags;  }; -#define TOMOYO_MAX_IO_READ_QUEUE 32 +/* Structure for "misc env" directive in domain policy. */ +struct tomoyo_env_acl { +	struct tomoyo_acl_info head;        /* type = TOMOYO_TYPE_ENV_ACL  */ +	const struct tomoyo_path_info *env; /* environment variable */ +}; + +/* Structure for "network inet" directive. */ +struct tomoyo_inet_acl { +	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_INET_ACL */ +	u8 protocol; +	u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */ +	struct tomoyo_ipaddr_union address; +	struct tomoyo_number_union port; +}; + +/* Structure for "network unix" directive. */ +struct tomoyo_unix_acl { +	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_UNIX_ACL */ +	u8 protocol; +	u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */ +	struct tomoyo_name_union name; +}; + +/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ +struct tomoyo_acl_param { +	char *data; +	struct list_head *list; +	struct tomoyo_policy_namespace *ns; +	bool is_delete; +}; + +#define TOMOYO_MAX_IO_READ_QUEUE 64  /*   * Structure for reading/writing policy via /sys/kernel/security/tomoyo @@ -535,98 +788,58 @@ struct tomoyo_mount_acl {  struct tomoyo_io_buffer {  	void (*read) (struct tomoyo_io_buffer *);  	int (*write) (struct tomoyo_io_buffer *); -	int (*poll) (struct file *file, poll_table *wait); +	unsigned int (*poll) (struct file *file, poll_table *wait);  	/* Exclusive lock for this structure.   */  	struct mutex io_sem; -	/* Index returned by tomoyo_read_lock(). */ -	int reader_idx;  	char __user *read_user_buf; -	int read_user_buf_avail; +	size_t read_user_buf_avail;  	struct { +		struct list_head *ns;  		struct list_head *domain;  		struct list_head *group;  		struct list_head *acl; -		int avail; -		int step; -		int query_index; +		size_t avail; +		unsigned int step; +		unsigned int query_index;  		u16 index; +		u16 cond_index; +		u8 acl_group_index; +		u8 cond_step;  		u8 bit;  		u8 w_pos;  		bool eof;  		bool print_this_domain_only; -		bool print_execute_only; +		bool print_transition_related_only; +		bool print_cond_part;  		const char *w[TOMOYO_MAX_IO_READ_QUEUE];  	} r; -	/* The position currently writing to.   */ -	struct tomoyo_domain_info *write_var1; +	struct { +		struct tomoyo_policy_namespace *ns; +		/* The position currently writing to.   */ +		struct tomoyo_domain_info *domain; +		/* Bytes available for writing.         */ +		size_t avail; +		bool is_delete; +	} w;  	/* Buffer for reading.                  */  	char *read_buf;  	/* Size of read buffer.                 */ -	int readbuf_size; +	size_t readbuf_size;  	/* Buffer for writing.                  */  	char *write_buf; -	/* Bytes available for writing.         */ -	int write_avail;  	/* Size of write buffer.                */ -	int writebuf_size; +	size_t writebuf_size;  	/* Type of this interface.              */ -	u8 type; -}; - -/* - * tomoyo_readable_file is a structure which is used for holding - * "allow_read" entries. - * It has following fields. - * - *  (1) "head" is "struct tomoyo_acl_head". - *  (2) "filename" is a pathname which is allowed to open(O_RDONLY). - */ -struct tomoyo_readable_file { -	struct tomoyo_acl_head head; -	const struct tomoyo_path_info *filename; -}; - -/* - * tomoyo_no_pattern is a structure which is used for holding - * "file_pattern" entries. - * It has following fields. - * - *  (1) "head" is "struct tomoyo_acl_head". - *  (2) "pattern" is a pathname pattern which is used for converting pathnames - *      to pathname patterns during learning mode. - */ -struct tomoyo_no_pattern { -	struct tomoyo_acl_head head; -	const struct tomoyo_path_info *pattern; -}; - -/* - * tomoyo_no_rewrite is a structure which is used for holding - * "deny_rewrite" entries. - * It has following fields. - * - *  (1) "head" is "struct tomoyo_acl_head". - *  (2) "pattern" is a pathname which is by default not permitted to modify - *      already existing content. - */ -struct tomoyo_no_rewrite { -	struct tomoyo_acl_head head; -	const struct tomoyo_path_info *pattern; +	enum tomoyo_securityfs_interface_index type; +	/* Users counter protected by tomoyo_io_buffer_list_lock. */ +	u8 users; +	/* List for telling GC not to kfree() elements. */ +	struct list_head list;  };  /* - * tomoyo_transition_control is a structure which is used for holding - * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" - * entries. - * It has following fields. - * - *  (1) "head" is "struct tomoyo_acl_head". - *  (2) "type" is type of this entry. - *  (3) "is_last_name" is a bool which is true if "domainname" is "the last - *      component of a domainname", false otherwise. - *  (4) "domainname" which is "a domainname" or "the last component of a - *      domainname". - *  (5) "program" which is a program's pathname. + * Structure for "initialize_domain"/"no_initialize_domain"/"keep_domain"/ + * "no_keep_domain" keyword.   */  struct tomoyo_transition_control {  	struct tomoyo_acl_head head; @@ -637,35 +850,16 @@ struct tomoyo_transition_control {  	const struct tomoyo_path_info *program;    /* Maybe NULL */  }; -/* - * tomoyo_aggregator is a structure which is used for holding - * "aggregator" entries. - * It has following fields. - * - *  (1) "head" is "struct tomoyo_acl_head". - *  (2) "original_name" which is originally requested name. - *  (3) "aggregated_name" which is name to rewrite. - */ +/* Structure for "aggregator" keyword. */  struct tomoyo_aggregator {  	struct tomoyo_acl_head head;  	const struct tomoyo_path_info *original_name;  	const struct tomoyo_path_info *aggregated_name;  }; -/* - * tomoyo_manager is a structure which is used for holding list of - * domainnames or programs which are permitted to modify configuration via - * /sys/kernel/security/tomoyo/ interface. - * It has following fields. - * - *  (1) "head" is "struct tomoyo_acl_head". - *  (2) "is_domain" is a bool which is true if "manager" is a domainname, false - *      otherwise. - *  (3) "manager" is a domainname or a program's pathname. - */ +/* Structure for policy manager. */  struct tomoyo_manager {  	struct tomoyo_acl_head head; -	bool is_domain;  /* True if manager is a domainname. */  	/* A path to program or a domainname. */  	const struct tomoyo_path_info *manager;  }; @@ -677,6 +871,7 @@ struct tomoyo_preference {  	bool permissive_verbose;  }; +/* Structure for /sys/kernel/security/tomnoyo/profile interface. */  struct tomoyo_profile {  	const struct tomoyo_path_info *comment;  	struct tomoyo_preference *learning; @@ -685,323 +880,443 @@ struct tomoyo_profile {  	struct tomoyo_preference preference;  	u8 default_config;  	u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX]; +	unsigned int pref[TOMOYO_MAX_PREF]; +}; + +/* Structure for representing YYYY/MM/DD hh/mm/ss. */ +struct tomoyo_time { +	u16 year; +	u8 month; +	u8 day; +	u8 hour; +	u8 min; +	u8 sec; +}; + +/* Structure for policy namespace. */ +struct tomoyo_policy_namespace { +	/* Profile table. Memory is allocated as needed. */ +	struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES]; +	/* List of "struct tomoyo_group". */ +	struct list_head group_list[TOMOYO_MAX_GROUP]; +	/* List of policy. */ +	struct list_head policy_list[TOMOYO_MAX_POLICY]; +	/* The global ACL referred by "use_group" keyword. */ +	struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS]; +	/* List for connecting to tomoyo_namespace_list list. */ +	struct list_head namespace_list; +	/* Profile version. Currently only 20110903 is defined. */ +	unsigned int profile_version; +	/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */ +	const char *name;  };  /********** Function prototypes. **********/ -/* Check whether the given string starts with the given keyword. */ -bool tomoyo_str_starts(char **src, const char *find); -/* Get tomoyo_realpath() of current process. */ -const char *tomoyo_get_exe(void); -/* Format string. */ -void tomoyo_normalize_line(unsigned char *buffer); -/* Print warning or error message on console. */ -void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) -     __attribute__ ((format(printf, 2, 3))); -/* Check all profiles currently assigned to domains are defined. */ -void tomoyo_check_profile(void); -/* Open operation for /sys/kernel/security/tomoyo/ interface. */ -int tomoyo_open_control(const u8 type, struct file *file); -/* Close /sys/kernel/security/tomoyo/ interface. */ -int tomoyo_close_control(struct file *file); -/* Poll operation for /sys/kernel/security/tomoyo/ interface. */ -int tomoyo_poll_control(struct file *file, poll_table *wait); -/* Read operation for /sys/kernel/security/tomoyo/ interface. */ -int tomoyo_read_control(struct file *file, char __user *buffer, -			const int buffer_len); -/* Write operation for /sys/kernel/security/tomoyo/ interface. */ -int tomoyo_write_control(struct file *file, const char __user *buffer, -			 const int buffer_len); -/* Check whether the domain has too many ACL entries to hold. */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); -/* Print out of memory warning message. */ -void tomoyo_warn_oom(const char *function); -/* Check whether the given name matches the given name_union. */ -const struct tomoyo_path_info * -tomoyo_compare_name_union(const struct tomoyo_path_info *name, -			  const struct tomoyo_name_union *ptr); -/* Check whether the given number matches the given number_union. */ +bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, +				  const struct tomoyo_group *group);  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); -void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) -	__attribute__ ((format(printf, 2, 3))); -/* Check whether the domainname is correct. */ +bool tomoyo_condition(struct tomoyo_request_info *r, +		      const struct tomoyo_condition *cond);  bool tomoyo_correct_domain(const unsigned char *domainname); -/* Check whether the token is correct. */  bool tomoyo_correct_path(const char *filename);  bool tomoyo_correct_word(const char *string); -/* Check whether the token can be a domainname. */  bool tomoyo_domain_def(const unsigned char *buffer); -bool tomoyo_parse_name_union(const char *filename, -			     struct tomoyo_name_union *ptr); -/* Check whether the given filename matches the given path_group. */ -const struct tomoyo_path_info * -tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, -			  const struct tomoyo_group *group); -/* Check whether the given value matches the given number_group. */ +bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); +bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, +		      struct tomoyo_page_dump *dump); +bool tomoyo_memory_ok(void *ptr);  bool tomoyo_number_matches_group(const unsigned long min,  				 const unsigned long max,  				 const struct tomoyo_group *group); -/* Check whether the given filename matches the given pattern. */ +bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param, +			       struct tomoyo_ipaddr_union *ptr); +bool tomoyo_parse_name_union(struct tomoyo_acl_param *param, +			     struct tomoyo_name_union *ptr); +bool tomoyo_parse_number_union(struct tomoyo_acl_param *param, +			       struct tomoyo_number_union *ptr);  bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,  				 const struct tomoyo_path_info *pattern); - -bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); -/* Tokenize a line. */ -bool tomoyo_tokenize(char *buffer, char *w[], size_t size); -/* Write domain policy violation warning message to console? */ -bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); -/* Fill "struct tomoyo_request_info". */ -int tomoyo_init_request_info(struct tomoyo_request_info *r, -			     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); -/* Create "aggregator" entry in exception policy. */ -int tomoyo_write_aggregator(char *data, const bool is_delete); -int tomoyo_write_transition_control(char *data, const bool is_delete, -				    const u8 type); -/* - * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", - * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", - * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar", - * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and - * "allow_link" entry in domain policy. - */ -int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, -		      const bool is_delete); -/* Create "allow_read" entry in exception policy. */ -int tomoyo_write_globally_readable(char *data, const bool is_delete); -/* Create "allow_mount" entry in domain policy. */ -int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, -		       const bool is_delete); -/* Create "deny_rewrite" entry in exception policy. */ -int tomoyo_write_no_rewrite(char *data, const bool is_delete); -/* Create "file_pattern" entry in exception policy. */ -int tomoyo_write_pattern(char *data, const bool is_delete); -/* Create "path_group"/"number_group" entry in exception policy. */ -int tomoyo_write_group(char *data, const bool is_delete, const u8 type); -int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) -     __attribute__ ((format(printf, 2, 3))); -/* Find a domain by the given name. */ -struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); -/* Find or create a domain by the given name. */ -struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, -						const u8 profile); -struct tomoyo_profile *tomoyo_profile(const u8 profile); -/* - * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". - */ -struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type); - -/* Check mode for specified functionality. */ -unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, -				const u8 index); -/* Fill in "struct tomoyo_path_info" members. */ -void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); -/* Run policy loader when /sbin/init starts. */ -void tomoyo_load_policy(const char *filename); - -void tomoyo_put_number_union(struct tomoyo_number_union *ptr); - -/* Convert binary string to ascii string. */ +bool tomoyo_permstr(const char *string, const char *keyword); +bool tomoyo_str_starts(char **src, const char *find);  char *tomoyo_encode(const char *str); - -/* - * Returns realpath(3) of the given pathname except that - * ignores chroot'ed root and does not follow the final symlink. - */ -char *tomoyo_realpath_nofollow(const char *pathname); -/* - * Returns realpath(3) of the given pathname except that - * ignores chroot'ed root and the pathname is already solved. - */ +char *tomoyo_encode2(const char *str, int str_len); +char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, +		      va_list args); +char *tomoyo_read_token(struct tomoyo_acl_param *param);  char *tomoyo_realpath_from_path(struct path *path); -/* Get patterned pathname. */ -const char *tomoyo_pattern(const struct tomoyo_path_info *filename); - -/* Check memory quota. */ -bool tomoyo_memory_ok(void *ptr); -void *tomoyo_commit_ok(void *data, const unsigned int size); - -/* - * Keep the given name on the RAM. - * The RAM is shared, so NEVER try to modify or kfree() the returned name. - */ +char *tomoyo_realpath_nofollow(const char *pathname); +const char *tomoyo_get_exe(void); +const char *tomoyo_yesno(const unsigned int value); +const struct tomoyo_path_info *tomoyo_compare_name_union +(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); +const struct tomoyo_path_info *tomoyo_get_domainname +(struct tomoyo_acl_param *param);  const struct tomoyo_path_info *tomoyo_get_name(const char *name); - -/* Check for memory usage. */ -void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); - -/* Set memory quota. */ -int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); - -/* Initialize mm related code. */ -void __init tomoyo_mm_init(void); -int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, -			   const struct tomoyo_path_info *filename); +const struct tomoyo_path_info *tomoyo_path_matches_group +(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);  int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,  				 struct path *path, const int flag); -int tomoyo_path_number_perm(const u8 operation, struct path *path, -			    unsigned long number); +void tomoyo_close_control(struct tomoyo_io_buffer *head); +int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); +int tomoyo_execute_permission(struct tomoyo_request_info *r, +			      const struct tomoyo_path_info *filename); +int tomoyo_find_next_domain(struct linux_binprm *bprm); +int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, +		    const u8 index); +int tomoyo_init_request_info(struct tomoyo_request_info *r, +			     struct tomoyo_domain_info *domain, +			     const u8 index);  int tomoyo_mkdev_perm(const u8 operation, struct path *path,  		      const unsigned int mode, unsigned int dev); -int tomoyo_path_perm(const u8 operation, struct path *path); +int tomoyo_mount_permission(const char *dev_name, struct path *path, +			    const char *type, unsigned long flags, +			    void *data_page); +int tomoyo_open_control(const u8 type, struct file *file);  int tomoyo_path2_perm(const u8 operation, struct path *path1,  		      struct path *path2); -int tomoyo_find_next_domain(struct linux_binprm *bprm); - -void tomoyo_print_ulong(char *buffer, const int buffer_len, -			const unsigned long value, const u8 type); - -/* Drop refcount on tomoyo_name_union. */ -void tomoyo_put_name_union(struct tomoyo_name_union *ptr); - -/* Run garbage collector. */ -void tomoyo_run_gc(void); - -void tomoyo_memory_free(void *ptr); - +int tomoyo_path_number_perm(const u8 operation, struct path *path, +			    unsigned long number); +int tomoyo_path_perm(const u8 operation, struct path *path, +		     const char *target); +unsigned int tomoyo_poll_control(struct file *file, poll_table *wait); +unsigned int tomoyo_poll_log(struct file *file, poll_table *wait); +int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, +				  int addr_len); +int tomoyo_socket_connect_permission(struct socket *sock, +				     struct sockaddr *addr, int addr_len); +int tomoyo_socket_listen_permission(struct socket *sock); +int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg, +				     int size); +int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) +	__printf(2, 3);  int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, -			 bool is_delete, struct tomoyo_domain_info *domain, -			 bool (*check_duplicate) (const struct tomoyo_acl_info -						  *, -						  const struct tomoyo_acl_info -						  *), -			 bool (*merge_duplicate) (struct tomoyo_acl_info *, -						  struct tomoyo_acl_info *, -						  const bool)); +			 struct tomoyo_acl_param *param, +			 bool (*check_duplicate) +			 (const struct tomoyo_acl_info *, +			  const struct tomoyo_acl_info *), +			 bool (*merge_duplicate) +			 (struct tomoyo_acl_info *, struct tomoyo_acl_info *, +			  const bool));  int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, -			 bool is_delete, struct list_head *list, -			 bool (*check_duplicate) (const struct tomoyo_acl_head -						  *, -						  const struct tomoyo_acl_head -						  *)); +			 struct tomoyo_acl_param *param, +			 bool (*check_duplicate) +			 (const struct tomoyo_acl_head *, +			  const struct tomoyo_acl_head *)); +int tomoyo_write_aggregator(struct tomoyo_acl_param *param); +int tomoyo_write_file(struct tomoyo_acl_param *param); +int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type); +int tomoyo_write_misc(struct tomoyo_acl_param *param); +int tomoyo_write_inet_network(struct tomoyo_acl_param *param); +int tomoyo_write_transition_control(struct tomoyo_acl_param *param, +				    const u8 type); +int tomoyo_write_unix_network(struct tomoyo_acl_param *param); +ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, 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_condition *tomoyo_get_condition(struct tomoyo_acl_param *param); +struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, +						const bool transit); +struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); +struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, +				      const u8 idx); +struct tomoyo_policy_namespace *tomoyo_assign_namespace +(const char *domainname); +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, +				      const u8 profile); +unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, +				const u8 index); +u8 tomoyo_parse_ulong(unsigned long *result, char **str); +void *tomoyo_commit_ok(void *data, const unsigned int size); +void __init tomoyo_load_builtin_policy(void); +void __init tomoyo_mm_init(void);  void tomoyo_check_acl(struct tomoyo_request_info *r,  		      bool (*check_entry) (struct tomoyo_request_info *,  					   const struct tomoyo_acl_info *)); +void tomoyo_check_profile(void); +void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp); +void tomoyo_del_condition(struct list_head *element); +void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); +void tomoyo_get_attributes(struct tomoyo_obj_info *obj); +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); +void tomoyo_load_policy(const char *filename); +void tomoyo_normalize_line(unsigned char *buffer); +void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); +void tomoyo_print_ip(char *buf, const unsigned int size, +		     const struct tomoyo_ipaddr_union *ptr); +void tomoyo_print_ulong(char *buffer, const int buffer_len, +			const unsigned long value, const u8 type); +void tomoyo_put_name_union(struct tomoyo_name_union *ptr); +void tomoyo_put_number_union(struct tomoyo_number_union *ptr); +void tomoyo_read_log(struct tomoyo_io_buffer *head); +void tomoyo_update_stat(const u8 index); +void tomoyo_warn_oom(const char *function); +void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) +	__printf(2, 3); +void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, +		       va_list args);  /********** External variable definitions. **********/ -/* Lock for GC. */ -extern struct srcu_struct tomoyo_ss; - -/* The list for "struct tomoyo_domain_info". */ +extern bool tomoyo_policy_loaded; +extern const char * const tomoyo_condition_keyword +[TOMOYO_MAX_CONDITION_KEYWORD]; +extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS]; +extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX +					      + TOMOYO_MAX_MAC_CATEGORY_INDEX]; +extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE]; +extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; +extern const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX]; +extern const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION]; +extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX]; +extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION]; +extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION]; +extern const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION]; +extern struct list_head tomoyo_condition_list;  extern struct list_head tomoyo_domain_list; - -extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; -extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];  extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; - -/* Lock for protecting policy. */ +extern struct list_head tomoyo_namespace_list;  extern struct mutex tomoyo_policy_lock; - -/* Has /sbin/init started? */ -extern bool tomoyo_policy_loaded; - -/* The kernel's domain. */ +extern struct srcu_struct tomoyo_ss;  extern struct tomoyo_domain_info tomoyo_kernel_domain; - -extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; -extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION]; -extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION]; -extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION]; - -extern unsigned int tomoyo_quota_for_query; -extern unsigned int tomoyo_query_memory_size; +extern struct tomoyo_policy_namespace tomoyo_kernel_namespace; +extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT]; +extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];  /********** Inlined functions. **********/ +/** + * tomoyo_read_lock - Take lock for protecting policy. + * + * Returns index number for tomoyo_read_unlock(). + */  static inline int tomoyo_read_lock(void)  {  	return srcu_read_lock(&tomoyo_ss);  } +/** + * tomoyo_read_unlock - Release lock for protecting policy. + * + * @idx: Index number returned by tomoyo_read_lock(). + * + * Returns nothing. + */  static inline void tomoyo_read_unlock(int idx)  {  	srcu_read_unlock(&tomoyo_ss, idx);  } -/* strcmp() for "struct tomoyo_path_info" structure. */ -static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, -				  const struct tomoyo_path_info *b) +/** + * tomoyo_sys_getppid - Copy of getppid(). + * + * Returns parent process's PID. + * + * Alpha does not have getppid() defined. To be able to build this module on + * Alpha, I have to copy getppid() from kernel/timer.c. + */ +static inline pid_t tomoyo_sys_getppid(void)  { -	return a->hash != b->hash || strcmp(a->name, b->name); +	pid_t pid; +	rcu_read_lock(); +	pid = task_tgid_vnr(rcu_dereference(current->real_parent)); +	rcu_read_unlock(); +	return pid;  }  /** - * tomoyo_valid - Check whether the character is a valid char. + * tomoyo_sys_getpid - Copy of getpid().   * - * @c: The character to check. + * Returns current thread's PID.   * - * Returns true if @c is a valid character, false otherwise. + * Alpha does not have getpid() defined. To be able to build this module on + * Alpha, I have to copy getpid() from kernel/timer.c.   */ -static inline bool tomoyo_valid(const unsigned char c) +static inline pid_t tomoyo_sys_getpid(void)  { -	return c > ' ' && c < 127; +	return task_tgid_vnr(current);  }  /** - * tomoyo_invalid - Check whether the character is an invalid char. + * tomoyo_pathcmp - strcmp() for "struct tomoyo_path_info" structure.   * - * @c: The character to check. + * @a: Pointer to "struct tomoyo_path_info". + * @b: Pointer to "struct tomoyo_path_info".   * - * Returns true if @c is an invalid character, false otherwise. + * Returns true if @a == @b, false otherwise.   */ -static inline bool tomoyo_invalid(const unsigned char c) +static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, +				  const struct tomoyo_path_info *b)  { -	return c && (c <= ' ' || c >= 127); +	return a->hash != b->hash || strcmp(a->name, b->name);  } +/** + * tomoyo_put_name - Drop reference on "struct tomoyo_name". + * + * @name: Pointer to "struct tomoyo_path_info". Maybe NULL. + * + * Returns nothing. + */  static inline void tomoyo_put_name(const struct tomoyo_path_info *name)  {  	if (name) {  		struct tomoyo_name *ptr =  			container_of(name, typeof(*ptr), entry); -		atomic_dec(&ptr->users); +		atomic_dec(&ptr->head.users);  	}  } +/** + * tomoyo_put_condition - Drop reference on "struct tomoyo_condition". + * + * @cond: Pointer to "struct tomoyo_condition". Maybe NULL. + * + * Returns nothing. + */ +static inline void tomoyo_put_condition(struct tomoyo_condition *cond) +{ +	if (cond) +		atomic_dec(&cond->head.users); +} + +/** + * tomoyo_put_group - Drop reference on "struct tomoyo_group". + * + * @group: Pointer to "struct tomoyo_group". Maybe NULL. + * + * Returns nothing. + */  static inline void tomoyo_put_group(struct tomoyo_group *group)  {  	if (group) -		atomic_dec(&group->users); +		atomic_dec(&group->head.users);  } +/** + * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread. + * + * Returns pointer to "struct tomoyo_domain_info" for current thread. + */  static inline struct tomoyo_domain_info *tomoyo_domain(void)  {  	return current_cred()->security;  } +/** + * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread. + * + * @task: Pointer to "struct task_struct". + * + * Returns pointer to "struct tomoyo_security" for specified thread. + */  static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct  							    *task)  {  	return task_cred_xxx(task, security);  } -static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1, -					   const struct tomoyo_acl_info *p2) +/** + * tomoyo_same_name_union - Check for duplicated "struct tomoyo_name_union" entry. + * + * @a: Pointer to "struct tomoyo_name_union". + * @b: Pointer to "struct tomoyo_name_union". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_name_union +(const struct tomoyo_name_union *a, const struct tomoyo_name_union *b)  { -	return p1->type == p2->type; +	return a->filename == b->filename && a->group == b->group;  } -static inline bool tomoyo_same_name_union -(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) +/** + * tomoyo_same_number_union - Check for duplicated "struct tomoyo_number_union" entry. + * + * @a: Pointer to "struct tomoyo_number_union". + * @b: Pointer to "struct tomoyo_number_union". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_number_union +(const struct tomoyo_number_union *a, const struct tomoyo_number_union *b)  { -	return p1->filename == p2->filename && p1->group == p2->group && -		p1->is_group == p2->is_group; +	return a->values[0] == b->values[0] && a->values[1] == b->values[1] && +		a->group == b->group && a->value_type[0] == b->value_type[0] && +		a->value_type[1] == b->value_type[1];  } -static inline bool tomoyo_same_number_union -(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) +/** + * tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry. + * + * @a: Pointer to "struct tomoyo_ipaddr_union". + * @b: Pointer to "struct tomoyo_ipaddr_union". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_ipaddr_union +(const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b) +{ +	return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group && +		a->is_ipv6 == b->is_ipv6; +} + +/** + * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread. + * + * Returns pointer to "struct tomoyo_policy_namespace" for current thread. + */ +static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void)  { -	return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1] -		&& p1->group == p2->group && p1->min_type == p2->min_type && -		p1->max_type == p2->max_type && p1->is_group == p2->is_group; +	return tomoyo_domain()->ns;  } +#if defined(CONFIG_SLOB) + +/** + * tomoyo_round2 - Round up to power of 2 for calculating memory usage. + * + * @size: Size to be rounded up. + * + * Returns @size. + * + * Since SLOB does not round up, this function simply returns @size. + */ +static inline int tomoyo_round2(size_t size) +{ +	return size; +} + +#else + +/** + * tomoyo_round2 - Round up to power of 2 for calculating memory usage. + * + * @size: Size to be rounded up. + * + * Returns rounded size. + * + * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of + * (e.g.) 128 bytes. + */ +static inline int tomoyo_round2(size_t size) +{ +#if PAGE_SIZE == 4096 +	size_t bsize = 32; +#else +	size_t bsize = 64; +#endif +	if (!size) +		return 0; +	while (size > bsize) +		bsize <<= 1; +	return bsize; +} + +#endif +  /**   * list_for_each_cookie - iterate over a list with cookie.   * @pos:        the &struct list_head to use as a loop cursor. diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c new file mode 100644 index 00000000000..63681e8be62 --- /dev/null +++ b/security/tomoyo/condition.c @@ -0,0 +1,1094 @@ +/* + * security/tomoyo/condition.c + * + * Copyright (C) 2005-2011  NTT DATA CORPORATION + */ + +#include "common.h" +#include <linux/slab.h> + +/* List of "struct tomoyo_condition". */ +LIST_HEAD(tomoyo_condition_list); + +/** + * tomoyo_argv - Check argv[] in "struct linux_binbrm". + * + * @index:   Index number of @arg_ptr. + * @arg_ptr: Contents of argv[@index]. + * @argc:    Length of @argv. + * @argv:    Pointer to "struct tomoyo_argv". + * @checked: Set to true if @argv[@index] was found. + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_argv(const unsigned int index, const char *arg_ptr, +			const int argc, const struct tomoyo_argv *argv, +			u8 *checked) +{ +	int i; +	struct tomoyo_path_info arg; +	arg.name = arg_ptr; +	for (i = 0; i < argc; argv++, checked++, i++) { +		bool result; +		if (index != argv->index) +			continue; +		*checked = 1; +		tomoyo_fill_path_info(&arg); +		result = tomoyo_path_matches_pattern(&arg, argv->value); +		if (argv->is_not) +			result = !result; +		if (!result) +			return false; +	} +	return true; +} + +/** + * tomoyo_envp - Check envp[] in "struct linux_binbrm". + * + * @env_name:  The name of environment variable. + * @env_value: The value of environment variable. + * @envc:      Length of @envp. + * @envp:      Pointer to "struct tomoyo_envp". + * @checked:   Set to true if @envp[@env_name] was found. + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_envp(const char *env_name, const char *env_value, +			const int envc, const struct tomoyo_envp *envp, +			u8 *checked) +{ +	int i; +	struct tomoyo_path_info name; +	struct tomoyo_path_info value; +	name.name = env_name; +	tomoyo_fill_path_info(&name); +	value.name = env_value; +	tomoyo_fill_path_info(&value); +	for (i = 0; i < envc; envp++, checked++, i++) { +		bool result; +		if (!tomoyo_path_matches_pattern(&name, envp->name)) +			continue; +		*checked = 1; +		if (envp->value) { +			result = tomoyo_path_matches_pattern(&value, +							     envp->value); +			if (envp->is_not) +				result = !result; +		} else { +			result = true; +			if (!envp->is_not) +				result = !result; +		} +		if (!result) +			return false; +	} +	return true; +} + +/** + * tomoyo_scan_bprm - Scan "struct linux_binprm". + * + * @ee:   Pointer to "struct tomoyo_execve". + * @argc: Length of @argc. + * @argv: Pointer to "struct tomoyo_argv". + * @envc: Length of @envp. + * @envp: Poiner to "struct tomoyo_envp". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_scan_bprm(struct tomoyo_execve *ee, +			     const u16 argc, const struct tomoyo_argv *argv, +			     const u16 envc, const struct tomoyo_envp *envp) +{ +	struct linux_binprm *bprm = ee->bprm; +	struct tomoyo_page_dump *dump = &ee->dump; +	char *arg_ptr = ee->tmp; +	int arg_len = 0; +	unsigned long pos = bprm->p; +	int offset = pos % PAGE_SIZE; +	int argv_count = bprm->argc; +	int envp_count = bprm->envc; +	bool result = true; +	u8 local_checked[32]; +	u8 *checked; +	if (argc + envc <= sizeof(local_checked)) { +		checked = local_checked; +		memset(local_checked, 0, sizeof(local_checked)); +	} else { +		checked = kzalloc(argc + envc, GFP_NOFS); +		if (!checked) +			return false; +	} +	while (argv_count || envp_count) { +		if (!tomoyo_dump_page(bprm, pos, dump)) { +			result = false; +			goto out; +		} +		pos += PAGE_SIZE - offset; +		while (offset < PAGE_SIZE) { +			/* Read. */ +			const char *kaddr = dump->data; +			const unsigned char c = kaddr[offset++]; +			if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) { +				if (c == '\\') { +					arg_ptr[arg_len++] = '\\'; +					arg_ptr[arg_len++] = '\\'; +				} else if (c > ' ' && c < 127) { +					arg_ptr[arg_len++] = c; +				} else { +					arg_ptr[arg_len++] = '\\'; +					arg_ptr[arg_len++] = (c >> 6) + '0'; +					arg_ptr[arg_len++] = +						((c >> 3) & 7) + '0'; +					arg_ptr[arg_len++] = (c & 7) + '0'; +				} +			} else { +				arg_ptr[arg_len] = '\0'; +			} +			if (c) +				continue; +			/* Check. */ +			if (argv_count) { +				if (!tomoyo_argv(bprm->argc - argv_count, +						 arg_ptr, argc, argv, +						 checked)) { +					result = false; +					break; +				} +				argv_count--; +			} else if (envp_count) { +				char *cp = strchr(arg_ptr, '='); +				if (cp) { +					*cp = '\0'; +					if (!tomoyo_envp(arg_ptr, cp + 1, +							 envc, envp, +							 checked + argc)) { +						result = false; +						break; +					} +				} +				envp_count--; +			} else { +				break; +			} +			arg_len = 0; +		} +		offset = 0; +		if (!result) +			break; +	} +out: +	if (result) { +		int i; +		/* Check not-yet-checked entries. */ +		for (i = 0; i < argc; i++) { +			if (checked[i]) +				continue; +			/* +			 * Return true only if all unchecked indexes in +			 * bprm->argv[] are not matched. +			 */ +			if (argv[i].is_not) +				continue; +			result = false; +			break; +		} +		for (i = 0; i < envc; envp++, i++) { +			if (checked[argc + i]) +				continue; +			/* +			 * Return true only if all unchecked environ variables +			 * in bprm->envp[] are either undefined or not matched. +			 */ +			if ((!envp->value && !envp->is_not) || +			    (envp->value && envp->is_not)) +				continue; +			result = false; +			break; +		} +	} +	if (checked != local_checked) +		kfree(checked); +	return result; +} + +/** + * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition". + * + * @file:  Pointer to "struct file". + * @ptr:   Pointer to "struct tomoyo_name_union". + * @match: True if "exec.realpath=", false if "exec.realpath!=". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_scan_exec_realpath(struct file *file, +				      const struct tomoyo_name_union *ptr, +				      const bool match) +{ +	bool result; +	struct tomoyo_path_info exe; +	if (!file) +		return false; +	exe.name = tomoyo_realpath_from_path(&file->f_path); +	if (!exe.name) +		return false; +	tomoyo_fill_path_info(&exe); +	result = tomoyo_compare_name_union(&exe, ptr); +	kfree(exe.name); +	return result == match; +} + +/** + * tomoyo_get_dqword - tomoyo_get_name() for a quoted string. + * + * @start: String to save. + * + * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. + */ +static const struct tomoyo_path_info *tomoyo_get_dqword(char *start) +{ +	char *cp = start + strlen(start) - 1; +	if (cp == start || *start++ != '"' || *cp != '"') +		return NULL; +	*cp = '\0'; +	if (*start && !tomoyo_correct_word(start)) +		return NULL; +	return tomoyo_get_name(start); +} + +/** + * tomoyo_parse_name_union_quoted - Parse a quoted word. + * + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr:   Pointer to "struct tomoyo_name_union". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param, +					   struct tomoyo_name_union *ptr) +{ +	char *filename = param->data; +	if (*filename == '@') +		return tomoyo_parse_name_union(param, ptr); +	ptr->filename = tomoyo_get_dqword(filename); +	return ptr->filename != NULL; +} + +/** + * tomoyo_parse_argv - Parse an argv[] condition part. + * + * @left:  Lefthand value. + * @right: Righthand value. + * @argv:  Pointer to "struct tomoyo_argv". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_parse_argv(char *left, char *right, +			      struct tomoyo_argv *argv) +{ +	if (tomoyo_parse_ulong(&argv->index, &left) != +	    TOMOYO_VALUE_TYPE_DECIMAL || *left++ != ']' || *left) +		return false; +	argv->value = tomoyo_get_dqword(right); +	return argv->value != NULL; +} + +/** + * tomoyo_parse_envp - Parse an envp[] condition part. + * + * @left:  Lefthand value. + * @right: Righthand value. + * @envp:  Pointer to "struct tomoyo_envp". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_parse_envp(char *left, char *right, +			      struct tomoyo_envp *envp) +{ +	const struct tomoyo_path_info *name; +	const struct tomoyo_path_info *value; +	char *cp = left + strlen(left) - 1; +	if (*cp-- != ']' || *cp != '"') +		goto out; +	*cp = '\0'; +	if (!tomoyo_correct_word(left)) +		goto out; +	name = tomoyo_get_name(left); +	if (!name) +		goto out; +	if (!strcmp(right, "NULL")) { +		value = NULL; +	} else { +		value = tomoyo_get_dqword(right); +		if (!value) { +			tomoyo_put_name(name); +			goto out; +		} +	} +	envp->name = name; +	envp->value = value; +	return true; +out: +	return false; +} + +/** + * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry. + * + * @a: Pointer to "struct tomoyo_condition". + * @b: Pointer to "struct tomoyo_condition". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, +					 const struct tomoyo_condition *b) +{ +	return a->size == b->size && a->condc == b->condc && +		a->numbers_count == b->numbers_count && +		a->names_count == b->names_count && +		a->argc == b->argc && a->envc == b->envc && +		a->grant_log == b->grant_log && a->transit == b->transit && +		!memcmp(a + 1, b + 1, a->size - sizeof(*a)); +} + +/** + * tomoyo_condition_type - Get condition type. + * + * @word: Keyword string. + * + * Returns one of values in "enum tomoyo_conditions_index" on success, + * TOMOYO_MAX_CONDITION_KEYWORD otherwise. + */ +static u8 tomoyo_condition_type(const char *word) +{ +	u8 i; +	for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) { +		if (!strcmp(word, tomoyo_condition_keyword[i])) +			break; +	} +	return i; +} + +/* Define this to enable debug mode. */ +/* #define DEBUG_CONDITION */ + +#ifdef DEBUG_CONDITION +#define dprintk printk +#else +#define dprintk(...) do { } while (0) +#endif + +/** + * tomoyo_commit_condition - Commit "struct tomoyo_condition". + * + * @entry: Pointer to "struct tomoyo_condition". + * + * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise. + * + * This function merges duplicated entries. This function returns NULL if + * @entry is not duplicated but memory quota for policy has exceeded. + */ +static struct tomoyo_condition *tomoyo_commit_condition +(struct tomoyo_condition *entry) +{ +	struct tomoyo_condition *ptr; +	bool found = false; +	if (mutex_lock_interruptible(&tomoyo_policy_lock)) { +		dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); +		ptr = NULL; +		found = true; +		goto out; +	} +	list_for_each_entry(ptr, &tomoyo_condition_list, head.list) { +		if (!tomoyo_same_condition(ptr, entry) || +		    atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) +			continue; +		/* Same entry found. Share this entry. */ +		atomic_inc(&ptr->head.users); +		found = true; +		break; +	} +	if (!found) { +		if (tomoyo_memory_ok(entry)) { +			atomic_set(&entry->head.users, 1); +			list_add(&entry->head.list, &tomoyo_condition_list); +		} else { +			found = true; +			ptr = NULL; +		} +	} +	mutex_unlock(&tomoyo_policy_lock); +out: +	if (found) { +		tomoyo_del_condition(&entry->head.list); +		kfree(entry); +		entry = ptr; +	} +	return entry; +} + +/** + * tomoyo_get_transit_preference - Parse domain transition preference for execve(). + * + * @param: Pointer to "struct tomoyo_acl_param". + * @e:     Pointer to "struct tomoyo_condition". + * + * Returns the condition string part. + */ +static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param, +					   struct tomoyo_condition *e) +{ +	char * const pos = param->data; +	bool flag; +	if (*pos == '<') { +		e->transit = tomoyo_get_domainname(param); +		goto done; +	} +	{ +		char *cp = strchr(pos, ' '); +		if (cp) +			*cp = '\0'; +		flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") || +			!strcmp(pos, "initialize") || !strcmp(pos, "reset") || +			!strcmp(pos, "child") || !strcmp(pos, "parent"); +		if (cp) +			*cp = ' '; +	} +	if (!flag) +		return pos; +	e->transit = tomoyo_get_name(tomoyo_read_token(param)); +done: +	if (e->transit) +		return param->data; +	/* +	 * Return a bad read-only condition string that will let +	 * tomoyo_get_condition() return NULL. +	 */ +	return "/"; +} + +/** + * tomoyo_get_condition - Parse condition part. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise. + */ +struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) +{ +	struct tomoyo_condition *entry = NULL; +	struct tomoyo_condition_element *condp = NULL; +	struct tomoyo_number_union *numbers_p = NULL; +	struct tomoyo_name_union *names_p = NULL; +	struct tomoyo_argv *argv = NULL; +	struct tomoyo_envp *envp = NULL; +	struct tomoyo_condition e = { }; +	char * const start_of_string = +		tomoyo_get_transit_preference(param, &e); +	char * const end_of_string = start_of_string + strlen(start_of_string); +	char *pos; +rerun: +	pos = start_of_string; +	while (1) { +		u8 left = -1; +		u8 right = -1; +		char *left_word = pos; +		char *cp; +		char *right_word; +		bool is_not; +		if (!*left_word) +			break; +		/* +		 * Since left-hand condition does not allow use of "path_group" +		 * or "number_group" and environment variable's names do not +		 * accept '=', it is guaranteed that the original line consists +		 * of one or more repetition of $left$operator$right blocks +		 * where "$left is free from '=' and ' '" and "$operator is +		 * either '=' or '!='" and "$right is free from ' '". +		 * Therefore, we can reconstruct the original line at the end +		 * of dry run even if we overwrite $operator with '\0'. +		 */ +		cp = strchr(pos, ' '); +		if (cp) { +			*cp = '\0'; /* Will restore later. */ +			pos = cp + 1; +		} else { +			pos = ""; +		} +		right_word = strchr(left_word, '='); +		if (!right_word || right_word == left_word) +			goto out; +		is_not = *(right_word - 1) == '!'; +		if (is_not) +			*(right_word++ - 1) = '\0'; /* Will restore later. */ +		else if (*(right_word + 1) != '=') +			*right_word++ = '\0'; /* Will restore later. */ +		else +			goto out; +		dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, +			is_not ? "!" : "", right_word); +		if (!strcmp(left_word, "grant_log")) { +			if (entry) { +				if (is_not || +				    entry->grant_log != TOMOYO_GRANTLOG_AUTO) +					goto out; +				else if (!strcmp(right_word, "yes")) +					entry->grant_log = TOMOYO_GRANTLOG_YES; +				else if (!strcmp(right_word, "no")) +					entry->grant_log = TOMOYO_GRANTLOG_NO; +				else +					goto out; +			} +			continue; +		} +		if (!strncmp(left_word, "exec.argv[", 10)) { +			if (!argv) { +				e.argc++; +				e.condc++; +			} else { +				e.argc--; +				e.condc--; +				left = TOMOYO_ARGV_ENTRY; +				argv->is_not = is_not; +				if (!tomoyo_parse_argv(left_word + 10, +						       right_word, argv++)) +					goto out; +			} +			goto store_value; +		} +		if (!strncmp(left_word, "exec.envp[\"", 11)) { +			if (!envp) { +				e.envc++; +				e.condc++; +			} else { +				e.envc--; +				e.condc--; +				left = TOMOYO_ENVP_ENTRY; +				envp->is_not = is_not; +				if (!tomoyo_parse_envp(left_word + 11, +						       right_word, envp++)) +					goto out; +			} +			goto store_value; +		} +		left = tomoyo_condition_type(left_word); +		dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word, +			left); +		if (left == TOMOYO_MAX_CONDITION_KEYWORD) { +			if (!numbers_p) { +				e.numbers_count++; +			} else { +				e.numbers_count--; +				left = TOMOYO_NUMBER_UNION; +				param->data = left_word; +				if (*left_word == '@' || +				    !tomoyo_parse_number_union(param, +							       numbers_p++)) +					goto out; +			} +		} +		if (!condp) +			e.condc++; +		else +			e.condc--; +		if (left == TOMOYO_EXEC_REALPATH || +		    left == TOMOYO_SYMLINK_TARGET) { +			if (!names_p) { +				e.names_count++; +			} else { +				e.names_count--; +				right = TOMOYO_NAME_UNION; +				param->data = right_word; +				if (!tomoyo_parse_name_union_quoted(param, +								    names_p++)) +					goto out; +			} +			goto store_value; +		} +		right = tomoyo_condition_type(right_word); +		if (right == TOMOYO_MAX_CONDITION_KEYWORD) { +			if (!numbers_p) { +				e.numbers_count++; +			} else { +				e.numbers_count--; +				right = TOMOYO_NUMBER_UNION; +				param->data = right_word; +				if (!tomoyo_parse_number_union(param, +							       numbers_p++)) +					goto out; +			} +		} +store_value: +		if (!condp) { +			dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " +				"match=%u\n", __LINE__, left, right, !is_not); +			continue; +		} +		condp->left = left; +		condp->right = right; +		condp->equals = !is_not; +		dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n", +			__LINE__, condp->left, condp->right, +			condp->equals); +		condp++; +	} +	dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n", +		__LINE__, e.condc, e.numbers_count, e.names_count, e.argc, +		e.envc); +	if (entry) { +		BUG_ON(e.names_count | e.numbers_count | e.argc | e.envc | +		       e.condc); +		return tomoyo_commit_condition(entry); +	} +	e.size = sizeof(*entry) +		+ e.condc * sizeof(struct tomoyo_condition_element) +		+ e.numbers_count * sizeof(struct tomoyo_number_union) +		+ e.names_count * sizeof(struct tomoyo_name_union) +		+ e.argc * sizeof(struct tomoyo_argv) +		+ e.envc * sizeof(struct tomoyo_envp); +	entry = kzalloc(e.size, GFP_NOFS); +	if (!entry) +		goto out2; +	*entry = e; +	e.transit = NULL; +	condp = (struct tomoyo_condition_element *) (entry + 1); +	numbers_p = (struct tomoyo_number_union *) (condp + e.condc); +	names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); +	argv = (struct tomoyo_argv *) (names_p + e.names_count); +	envp = (struct tomoyo_envp *) (argv + e.argc); +	{ +		bool flag = false; +		for (pos = start_of_string; pos < end_of_string; pos++) { +			if (*pos) +				continue; +			if (flag) /* Restore " ". */ +				*pos = ' '; +			else if (*(pos + 1) == '=') /* Restore "!=". */ +				*pos = '!'; +			else /* Restore "=". */ +				*pos = '='; +			flag = !flag; +		} +	} +	goto rerun; +out: +	dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); +	if (entry) { +		tomoyo_del_condition(&entry->head.list); +		kfree(entry); +	} +out2: +	tomoyo_put_name(e.transit); +	return NULL; +} + +/** + * tomoyo_get_attributes - Revalidate "struct inode". + * + * @obj: Pointer to "struct tomoyo_obj_info". + * + * Returns nothing. + */ +void tomoyo_get_attributes(struct tomoyo_obj_info *obj) +{ +	u8 i; +	struct dentry *dentry = NULL; + +	for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) { +		struct inode *inode; +		switch (i) { +		case TOMOYO_PATH1: +			dentry = obj->path1.dentry; +			if (!dentry) +				continue; +			break; +		case TOMOYO_PATH2: +			dentry = obj->path2.dentry; +			if (!dentry) +				continue; +			break; +		default: +			if (!dentry) +				continue; +			dentry = dget_parent(dentry); +			break; +		} +		inode = dentry->d_inode; +		if (inode) { +			struct tomoyo_mini_stat *stat = &obj->stat[i]; +			stat->uid  = inode->i_uid; +			stat->gid  = inode->i_gid; +			stat->ino  = inode->i_ino; +			stat->mode = inode->i_mode; +			stat->dev  = inode->i_sb->s_dev; +			stat->rdev = inode->i_rdev; +			obj->stat_valid[i] = true; +		} +		if (i & 1) /* i == TOMOYO_PATH1_PARENT || +			      i == TOMOYO_PATH2_PARENT */ +			dput(dentry); +	} +} + +/** + * tomoyo_condition - Check condition part. + * + * @r:    Pointer to "struct tomoyo_request_info". + * @cond: Pointer to "struct tomoyo_condition". Maybe NULL. + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_condition(struct tomoyo_request_info *r, +		      const struct tomoyo_condition *cond) +{ +	u32 i; +	unsigned long min_v[2] = { 0, 0 }; +	unsigned long max_v[2] = { 0, 0 }; +	const struct tomoyo_condition_element *condp; +	const struct tomoyo_number_union *numbers_p; +	const struct tomoyo_name_union *names_p; +	const struct tomoyo_argv *argv; +	const struct tomoyo_envp *envp; +	struct tomoyo_obj_info *obj; +	u16 condc; +	u16 argc; +	u16 envc; +	struct linux_binprm *bprm = NULL; +	if (!cond) +		return true; +	condc = cond->condc; +	argc = cond->argc; +	envc = cond->envc; +	obj = r->obj; +	if (r->ee) +		bprm = r->ee->bprm; +	if (!bprm && (argc || envc)) +		return false; +	condp = (struct tomoyo_condition_element *) (cond + 1); +	numbers_p = (const struct tomoyo_number_union *) (condp + condc); +	names_p = (const struct tomoyo_name_union *) +		(numbers_p + cond->numbers_count); +	argv = (const struct tomoyo_argv *) (names_p + cond->names_count); +	envp = (const struct tomoyo_envp *) (argv + argc); +	for (i = 0; i < condc; i++) { +		const bool match = condp->equals; +		const u8 left = condp->left; +		const u8 right = condp->right; +		bool is_bitop[2] = { false, false }; +		u8 j; +		condp++; +		/* Check argv[] and envp[] later. */ +		if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY) +			continue; +		/* Check string expressions. */ +		if (right == TOMOYO_NAME_UNION) { +			const struct tomoyo_name_union *ptr = names_p++; +			switch (left) { +				struct tomoyo_path_info *symlink; +				struct tomoyo_execve *ee; +				struct file *file; +			case TOMOYO_SYMLINK_TARGET: +				symlink = obj ? obj->symlink_target : NULL; +				if (!symlink || +				    !tomoyo_compare_name_union(symlink, ptr) +				    == match) +					goto out; +				break; +			case TOMOYO_EXEC_REALPATH: +				ee = r->ee; +				file = ee ? ee->bprm->file : NULL; +				if (!tomoyo_scan_exec_realpath(file, ptr, +							       match)) +					goto out; +				break; +			} +			continue; +		} +		/* Check numeric or bit-op expressions. */ +		for (j = 0; j < 2; j++) { +			const u8 index = j ? right : left; +			unsigned long value = 0; +			switch (index) { +			case TOMOYO_TASK_UID: +				value = from_kuid(&init_user_ns, current_uid()); +				break; +			case TOMOYO_TASK_EUID: +				value = from_kuid(&init_user_ns, current_euid()); +				break; +			case TOMOYO_TASK_SUID: +				value = from_kuid(&init_user_ns, current_suid()); +				break; +			case TOMOYO_TASK_FSUID: +				value = from_kuid(&init_user_ns, current_fsuid()); +				break; +			case TOMOYO_TASK_GID: +				value = from_kgid(&init_user_ns, current_gid()); +				break; +			case TOMOYO_TASK_EGID: +				value = from_kgid(&init_user_ns, current_egid()); +				break; +			case TOMOYO_TASK_SGID: +				value = from_kgid(&init_user_ns, current_sgid()); +				break; +			case TOMOYO_TASK_FSGID: +				value = from_kgid(&init_user_ns, current_fsgid()); +				break; +			case TOMOYO_TASK_PID: +				value = tomoyo_sys_getpid(); +				break; +			case TOMOYO_TASK_PPID: +				value = tomoyo_sys_getppid(); +				break; +			case TOMOYO_TYPE_IS_SOCKET: +				value = S_IFSOCK; +				break; +			case TOMOYO_TYPE_IS_SYMLINK: +				value = S_IFLNK; +				break; +			case TOMOYO_TYPE_IS_FILE: +				value = S_IFREG; +				break; +			case TOMOYO_TYPE_IS_BLOCK_DEV: +				value = S_IFBLK; +				break; +			case TOMOYO_TYPE_IS_DIRECTORY: +				value = S_IFDIR; +				break; +			case TOMOYO_TYPE_IS_CHAR_DEV: +				value = S_IFCHR; +				break; +			case TOMOYO_TYPE_IS_FIFO: +				value = S_IFIFO; +				break; +			case TOMOYO_MODE_SETUID: +				value = S_ISUID; +				break; +			case TOMOYO_MODE_SETGID: +				value = S_ISGID; +				break; +			case TOMOYO_MODE_STICKY: +				value = S_ISVTX; +				break; +			case TOMOYO_MODE_OWNER_READ: +				value = S_IRUSR; +				break; +			case TOMOYO_MODE_OWNER_WRITE: +				value = S_IWUSR; +				break; +			case TOMOYO_MODE_OWNER_EXECUTE: +				value = S_IXUSR; +				break; +			case TOMOYO_MODE_GROUP_READ: +				value = S_IRGRP; +				break; +			case TOMOYO_MODE_GROUP_WRITE: +				value = S_IWGRP; +				break; +			case TOMOYO_MODE_GROUP_EXECUTE: +				value = S_IXGRP; +				break; +			case TOMOYO_MODE_OTHERS_READ: +				value = S_IROTH; +				break; +			case TOMOYO_MODE_OTHERS_WRITE: +				value = S_IWOTH; +				break; +			case TOMOYO_MODE_OTHERS_EXECUTE: +				value = S_IXOTH; +				break; +			case TOMOYO_EXEC_ARGC: +				if (!bprm) +					goto out; +				value = bprm->argc; +				break; +			case TOMOYO_EXEC_ENVC: +				if (!bprm) +					goto out; +				value = bprm->envc; +				break; +			case TOMOYO_NUMBER_UNION: +				/* Fetch values later. */ +				break; +			default: +				if (!obj) +					goto out; +				if (!obj->validate_done) { +					tomoyo_get_attributes(obj); +					obj->validate_done = true; +				} +				{ +					u8 stat_index; +					struct tomoyo_mini_stat *stat; +					switch (index) { +					case TOMOYO_PATH1_UID: +					case TOMOYO_PATH1_GID: +					case TOMOYO_PATH1_INO: +					case TOMOYO_PATH1_MAJOR: +					case TOMOYO_PATH1_MINOR: +					case TOMOYO_PATH1_TYPE: +					case TOMOYO_PATH1_DEV_MAJOR: +					case TOMOYO_PATH1_DEV_MINOR: +					case TOMOYO_PATH1_PERM: +						stat_index = TOMOYO_PATH1; +						break; +					case TOMOYO_PATH2_UID: +					case TOMOYO_PATH2_GID: +					case TOMOYO_PATH2_INO: +					case TOMOYO_PATH2_MAJOR: +					case TOMOYO_PATH2_MINOR: +					case TOMOYO_PATH2_TYPE: +					case TOMOYO_PATH2_DEV_MAJOR: +					case TOMOYO_PATH2_DEV_MINOR: +					case TOMOYO_PATH2_PERM: +						stat_index = TOMOYO_PATH2; +						break; +					case TOMOYO_PATH1_PARENT_UID: +					case TOMOYO_PATH1_PARENT_GID: +					case TOMOYO_PATH1_PARENT_INO: +					case TOMOYO_PATH1_PARENT_PERM: +						stat_index = +							TOMOYO_PATH1_PARENT; +						break; +					case TOMOYO_PATH2_PARENT_UID: +					case TOMOYO_PATH2_PARENT_GID: +					case TOMOYO_PATH2_PARENT_INO: +					case TOMOYO_PATH2_PARENT_PERM: +						stat_index = +							TOMOYO_PATH2_PARENT; +						break; +					default: +						goto out; +					} +					if (!obj->stat_valid[stat_index]) +						goto out; +					stat = &obj->stat[stat_index]; +					switch (index) { +					case TOMOYO_PATH1_UID: +					case TOMOYO_PATH2_UID: +					case TOMOYO_PATH1_PARENT_UID: +					case TOMOYO_PATH2_PARENT_UID: +						value = from_kuid(&init_user_ns, stat->uid); +						break; +					case TOMOYO_PATH1_GID: +					case TOMOYO_PATH2_GID: +					case TOMOYO_PATH1_PARENT_GID: +					case TOMOYO_PATH2_PARENT_GID: +						value = from_kgid(&init_user_ns, stat->gid); +						break; +					case TOMOYO_PATH1_INO: +					case TOMOYO_PATH2_INO: +					case TOMOYO_PATH1_PARENT_INO: +					case TOMOYO_PATH2_PARENT_INO: +						value = stat->ino; +						break; +					case TOMOYO_PATH1_MAJOR: +					case TOMOYO_PATH2_MAJOR: +						value = MAJOR(stat->dev); +						break; +					case TOMOYO_PATH1_MINOR: +					case TOMOYO_PATH2_MINOR: +						value = MINOR(stat->dev); +						break; +					case TOMOYO_PATH1_TYPE: +					case TOMOYO_PATH2_TYPE: +						value = stat->mode & S_IFMT; +						break; +					case TOMOYO_PATH1_DEV_MAJOR: +					case TOMOYO_PATH2_DEV_MAJOR: +						value = MAJOR(stat->rdev); +						break; +					case TOMOYO_PATH1_DEV_MINOR: +					case TOMOYO_PATH2_DEV_MINOR: +						value = MINOR(stat->rdev); +						break; +					case TOMOYO_PATH1_PERM: +					case TOMOYO_PATH2_PERM: +					case TOMOYO_PATH1_PARENT_PERM: +					case TOMOYO_PATH2_PARENT_PERM: +						value = stat->mode & S_IALLUGO; +						break; +					} +				} +				break; +			} +			max_v[j] = value; +			min_v[j] = value; +			switch (index) { +			case TOMOYO_MODE_SETUID: +			case TOMOYO_MODE_SETGID: +			case TOMOYO_MODE_STICKY: +			case TOMOYO_MODE_OWNER_READ: +			case TOMOYO_MODE_OWNER_WRITE: +			case TOMOYO_MODE_OWNER_EXECUTE: +			case TOMOYO_MODE_GROUP_READ: +			case TOMOYO_MODE_GROUP_WRITE: +			case TOMOYO_MODE_GROUP_EXECUTE: +			case TOMOYO_MODE_OTHERS_READ: +			case TOMOYO_MODE_OTHERS_WRITE: +			case TOMOYO_MODE_OTHERS_EXECUTE: +				is_bitop[j] = true; +			} +		} +		if (left == TOMOYO_NUMBER_UNION) { +			/* Fetch values now. */ +			const struct tomoyo_number_union *ptr = numbers_p++; +			min_v[0] = ptr->values[0]; +			max_v[0] = ptr->values[1]; +		} +		if (right == TOMOYO_NUMBER_UNION) { +			/* Fetch values now. */ +			const struct tomoyo_number_union *ptr = numbers_p++; +			if (ptr->group) { +				if (tomoyo_number_matches_group(min_v[0], +								max_v[0], +								ptr->group) +				    == match) +					continue; +			} else { +				if ((min_v[0] <= ptr->values[1] && +				     max_v[0] >= ptr->values[0]) == match) +					continue; +			} +			goto out; +		} +		/* +		 * Bit operation is valid only when counterpart value +		 * represents permission. +		 */ +		if (is_bitop[0] && is_bitop[1]) { +			goto out; +		} else if (is_bitop[0]) { +			switch (right) { +			case TOMOYO_PATH1_PERM: +			case TOMOYO_PATH1_PARENT_PERM: +			case TOMOYO_PATH2_PERM: +			case TOMOYO_PATH2_PARENT_PERM: +				if (!(max_v[0] & max_v[1]) == !match) +					continue; +			} +			goto out; +		} else if (is_bitop[1]) { +			switch (left) { +			case TOMOYO_PATH1_PERM: +			case TOMOYO_PATH1_PARENT_PERM: +			case TOMOYO_PATH2_PERM: +			case TOMOYO_PATH2_PARENT_PERM: +				if (!(max_v[0] & max_v[1]) == !match) +					continue; +			} +			goto out; +		} +		/* Normal value range comparison. */ +		if ((min_v[0] <= max_v[1] && max_v[0] >= min_v[1]) == match) +			continue; +out: +		return false; +	} +	/* Check argv[] and envp[] now. */ +	if (r->ee && (argc || envc)) +		return tomoyo_scan_bprm(r->ee, argc, argv, envc, envp); +	return true; +} diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 35388408e47..38651454ed0 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -1,9 +1,7 @@  /*   * security/tomoyo/domain.c   * - * Domain transition functions for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include "common.h" @@ -20,8 +18,7 @@ struct tomoyo_domain_info tomoyo_kernel_domain;   *   * @new_entry:       Pointer to "struct tomoyo_acl_info".   * @size:            Size of @new_entry in bytes. - * @is_delete:       True if it is a delete request. - * @list:            Pointer to "struct list_head". + * @param:           Pointer to "struct tomoyo_acl_param".   * @check_duplicate: Callback function to find duplicated entry.   *   * Returns 0 on success, negative value otherwise. @@ -29,25 +26,28 @@ struct tomoyo_domain_info tomoyo_kernel_domain;   * Caller holds tomoyo_read_lock().   */  int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, -			 bool is_delete, struct list_head *list, +			 struct tomoyo_acl_param *param,  			 bool (*check_duplicate) (const struct tomoyo_acl_head  						  *,  						  const struct tomoyo_acl_head  						  *))  { -	int error = is_delete ? -ENOENT : -ENOMEM; +	int error = param->is_delete ? -ENOENT : -ENOMEM;  	struct tomoyo_acl_head *entry; +	struct list_head *list = param->list;  	if (mutex_lock_interruptible(&tomoyo_policy_lock))  		return -ENOMEM;  	list_for_each_entry_rcu(entry, list, list) { +		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) +			continue;  		if (!check_duplicate(entry, new_entry))  			continue; -		entry->is_deleted = is_delete; +		entry->is_deleted = param->is_delete;  		error = 0;  		break;  	} -	if (error && !is_delete) { +	if (error && !param->is_delete) {  		entry = tomoyo_commit_ok(new_entry, size);  		if (entry) {  			list_add_tail_rcu(&entry->list, list); @@ -59,12 +59,25 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,  }  /** + * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a, +					const struct tomoyo_acl_info *b) +{ +	return a->type == b->type && a->cond == b->cond; +} + +/**   * tomoyo_update_domain - Update an entry for domain policy.   *   * @new_entry:       Pointer to "struct tomoyo_acl_info".   * @size:            Size of @new_entry in bytes. - * @is_delete:       True if it is a delete request. - * @domain:          Pointer to "struct tomoyo_domain_info". + * @param:           Pointer to "struct tomoyo_acl_param".   * @check_duplicate: Callback function to find duplicated entry.   * @merge_duplicate: Callback function to merge duplicated entry.   * @@ -73,7 +86,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,   * Caller holds tomoyo_read_lock().   */  int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, -			 bool is_delete, struct tomoyo_domain_info *domain, +			 struct tomoyo_acl_param *param,  			 bool (*check_duplicate) (const struct tomoyo_acl_info  						  *,  						  const struct tomoyo_acl_info @@ -82,13 +95,32 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,  						  struct tomoyo_acl_info *,  						  const bool))  { +	const bool is_delete = param->is_delete;  	int error = is_delete ? -ENOENT : -ENOMEM;  	struct tomoyo_acl_info *entry; +	struct list_head * const list = param->list; +	if (param->data[0]) { +		new_entry->cond = tomoyo_get_condition(param); +		if (!new_entry->cond) +			return -EINVAL; +		/* +		 * Domain transition preference is allowed for only +		 * "file execute" entries. +		 */ +		if (new_entry->cond->transit && +		    !(new_entry->type == TOMOYO_TYPE_PATH_ACL && +		      container_of(new_entry, struct tomoyo_path_acl, head) +		      ->perm == 1 << TOMOYO_TYPE_EXECUTE)) +			goto out; +	}  	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		return error; -	list_for_each_entry_rcu(entry, &domain->acl_info_list, list) { -		if (!check_duplicate(entry, new_entry)) +		goto out; +	list_for_each_entry_rcu(entry, list, list) { +		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) +			continue; +		if (!tomoyo_same_acl_head(entry, new_entry) || +		    !check_duplicate(entry, new_entry))  			continue;  		if (merge_duplicate)  			entry->is_deleted = merge_duplicate(entry, new_entry, @@ -101,28 +133,51 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,  	if (error && !is_delete) {  		entry = tomoyo_commit_ok(new_entry, size);  		if (entry) { -			list_add_tail_rcu(&entry->list, &domain->acl_info_list); +			list_add_tail_rcu(&entry->list, list);  			error = 0;  		}  	}  	mutex_unlock(&tomoyo_policy_lock); +out: +	tomoyo_put_condition(new_entry->cond);  	return error;  } +/** + * tomoyo_check_acl - Do permission check. + * + * @r:           Pointer to "struct tomoyo_request_info". + * @check_entry: Callback function to check type specific parameters. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */  void tomoyo_check_acl(struct tomoyo_request_info *r,  		      bool (*check_entry) (struct tomoyo_request_info *,  					   const struct tomoyo_acl_info *))  {  	const struct tomoyo_domain_info *domain = r->domain;  	struct tomoyo_acl_info *ptr; +	bool retried = false; +	const struct list_head *list = &domain->acl_info_list; -	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { +retry: +	list_for_each_entry_rcu(ptr, list, list) {  		if (ptr->is_deleted || ptr->type != r->param_type)  			continue; -		if (check_entry(r, ptr)) { -			r->granted = true; -			return; -		} +		if (!check_entry(r, ptr)) +			continue; +		if (!tomoyo_condition(r, ptr->cond)) +			continue; +		r->matched_acl = ptr; +		r->granted = true; +		return; +	} +	if (!retried) { +		retried = true; +		list = &domain->ns->acl_group[domain->group]; +		goto retry;  	}  	r->granted = false;  } @@ -130,24 +185,29 @@ void tomoyo_check_acl(struct tomoyo_request_info *r,  /* The list for "struct tomoyo_domain_info". */  LIST_HEAD(tomoyo_domain_list); -struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; -struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; -  /**   * tomoyo_last_word - Get last component of a domainname.   * - * @domainname: Domainname to check. + * @name: Domainname to check.   *   * Returns the last word of @domainname.   */  static const char *tomoyo_last_word(const char *name)  { -        const char *cp = strrchr(name, ' '); -        if (cp) -                return cp + 1; -        return name; +	const char *cp = strrchr(name, ' '); +	if (cp) +		return cp + 1; +	return name;  } +/** + * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" 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_transition_control(const struct tomoyo_acl_head *a,  					   const struct tomoyo_acl_head *b)  { @@ -163,30 +223,36 @@ static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,  }  /** - * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. + * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.   * - * @domainname: The name of domain. Maybe NULL. - * @program:    The name of program. Maybe NULL. - * @type:       Type of transition. - * @is_delete:  True if it is a delete request. + * @param: Pointer to "struct tomoyo_acl_param". + * @type:  Type of this entry.   *   * Returns 0 on success, negative value otherwise.   */ -static int tomoyo_update_transition_control_entry(const char *domainname, -						  const char *program, -						  const u8 type, -						  const bool is_delete) +int tomoyo_write_transition_control(struct tomoyo_acl_param *param, +				    const u8 type)  {  	struct tomoyo_transition_control e = { .type = type }; -	int error = is_delete ? -ENOENT : -ENOMEM; -	if (program) { +	int error = param->is_delete ? -ENOENT : -ENOMEM; +	char *program = param->data; +	char *domainname = strstr(program, " from "); +	if (domainname) { +		*domainname = '\0'; +		domainname += 6; +	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || +		   type == TOMOYO_TRANSITION_CONTROL_KEEP) { +		domainname = program; +		program = NULL; +	} +	if (program && strcmp(program, "any")) {  		if (!tomoyo_correct_path(program))  			return -EINVAL;  		e.program = tomoyo_get_name(program);  		if (!e.program)  			goto out;  	} -	if (domainname) { +	if (domainname && strcmp(domainname, "any")) {  		if (!tomoyo_correct_domain(domainname)) {  			if (!tomoyo_correct_path(domainname))  				goto out; @@ -196,126 +262,136 @@ static int tomoyo_update_transition_control_entry(const char *domainname,  		if (!e.domainname)  			goto out;  	} -	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -				     &tomoyo_policy_list -				     [TOMOYO_ID_TRANSITION_CONTROL], +	param->list = ¶m->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; +	error = tomoyo_update_policy(&e.head, sizeof(e), param,  				     tomoyo_same_transition_control); - out: +out:  	tomoyo_put_name(e.domainname);  	tomoyo_put_name(e.program);  	return error;  }  /** - * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. + * tomoyo_scan_transition - Try to find specific domain transition type.   * - * @data:      String to parse. - * @is_delete: True if it is a delete request. - * @type:      Type of this entry. + * @list:       Pointer to "struct list_head". + * @domainname: The name of current domain. + * @program:    The name of requested program. + * @last_name:  The last component of @domainname. + * @type:       One of values in "enum tomoyo_transition_type".   * - * Returns 0 on success, negative value otherwise. + * Returns true if found one, false otherwise. + * + * Caller holds tomoyo_read_lock().   */ -int tomoyo_write_transition_control(char *data, const bool is_delete, -				    const u8 type) +static inline bool tomoyo_scan_transition +(const struct list_head *list, const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program, const char *last_name, + const enum tomoyo_transition_type type)  { -	char *domainname = strstr(data, " from "); -	if (domainname) { -		*domainname = '\0'; -		domainname += 6; -	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || -		   type == TOMOYO_TRANSITION_CONTROL_KEEP) { -		domainname = data; -		data = NULL; +	const struct tomoyo_transition_control *ptr; +	list_for_each_entry_rcu(ptr, list, head.list) { +		if (ptr->head.is_deleted || ptr->type != type) +			continue; +		if (ptr->domainname) { +			if (!ptr->is_last_name) { +				if (ptr->domainname != domainname) +					continue; +			} else { +				/* +				 * Use direct strcmp() since this is +				 * unlikely used. +				 */ +				if (strcmp(ptr->domainname->name, last_name)) +					continue; +			} +		} +		if (ptr->program && tomoyo_pathcmp(ptr->program, program)) +			continue; +		return true;  	} -	return tomoyo_update_transition_control_entry(domainname, data, type, -						      is_delete); +	return false;  }  /**   * tomoyo_transition_type - Get domain transition type.   * - * @domainname: The name of domain. - * @program:    The name of program. + * @ns:         Pointer to "struct tomoyo_policy_namespace". + * @domainname: The name of current domain. + * @program:    The name of requested program.   * - * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program - * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing - * @program suppresses domain transition, others otherwise. + * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes + * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if + * executing @program reinitializes domain transition within that namespace, + * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname , + * others otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, -				 const struct tomoyo_path_info *program) +static enum tomoyo_transition_type tomoyo_transition_type +(const struct tomoyo_policy_namespace *ns, + const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program)  { -	const struct tomoyo_transition_control *ptr;  	const char *last_name = tomoyo_last_word(domainname->name); -	u8 type; -	for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { - next: -		list_for_each_entry_rcu(ptr, &tomoyo_policy_list -					[TOMOYO_ID_TRANSITION_CONTROL], -					head.list) { -			if (ptr->head.is_deleted || ptr->type != type) -				continue; -			if (ptr->domainname) { -				if (!ptr->is_last_name) { -					if (ptr->domainname != domainname) -						continue; -				} else { -					/* -					 * Use direct strcmp() since this is -					 * unlikely used. -					 */ -					if (strcmp(ptr->domainname->name, -						   last_name)) -						continue; -				} -			} -			if (ptr->program && -			    tomoyo_pathcmp(ptr->program, program)) -				continue; -			if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { -				/* -				 * Do not check for initialize_domain if -				 * no_initialize_domain matched. -				 */ -				type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; -				goto next; -			} -			goto done; +	enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET; +	while (type < TOMOYO_MAX_TRANSITION_TYPE) { +		const struct list_head * const list = +			&ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; +		if (!tomoyo_scan_transition(list, domainname, program, +					    last_name, type)) { +			type++; +			continue;  		} +		if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET && +		    type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) +			break; +		/* +		 * Do not check for reset_domain if no_reset_domain matched. +		 * Do not check for initialize_domain if no_initialize_domain +		 * matched. +		 */ +		type++; +		type++;  	} - done:  	return type;  } +/** + * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" 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_aggregator(const struct tomoyo_acl_head *a,  				   const struct tomoyo_acl_head *b)  { -	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head); -	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); +	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), +							  head); +	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), +							  head);  	return p1->original_name == p2->original_name &&  		p1->aggregated_name == p2->aggregated_name;  }  /** - * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list. + * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.   * - * @original_name:   The original program's name. - * @aggregated_name: The program name to use. - * @is_delete:       True if it is a delete request. + * @param: Pointer to "struct tomoyo_acl_param".   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_update_aggregator_entry(const char *original_name, -					  const char *aggregated_name, -					  const bool is_delete) +int tomoyo_write_aggregator(struct tomoyo_acl_param *param)  {  	struct tomoyo_aggregator e = { }; -	int error = is_delete ? -ENOENT : -ENOMEM; - -	if (!tomoyo_correct_path(original_name) || +	int error = param->is_delete ? -ENOENT : -ENOMEM; +	const char *original_name = tomoyo_read_token(param); +	const char *aggregated_name = tomoyo_read_token(param); +	if (!tomoyo_correct_word(original_name) ||  	    !tomoyo_correct_path(aggregated_name))  		return -EINVAL;  	e.original_name = tomoyo_get_name(original_name); @@ -323,83 +399,269 @@ static int tomoyo_update_aggregator_entry(const char *original_name,  	if (!e.original_name || !e.aggregated_name ||  	    e.aggregated_name->is_patterned) /* No patterns allowed. */  		goto out; -	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -				     &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], +	param->list = ¶m->ns->policy_list[TOMOYO_ID_AGGREGATOR]; +	error = tomoyo_update_policy(&e.head, sizeof(e), param,  				     tomoyo_same_aggregator); - out: +out:  	tomoyo_put_name(e.original_name);  	tomoyo_put_name(e.aggregated_name);  	return error;  }  /** - * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. + * tomoyo_find_namespace - Find specified namespace.   * - * @data:      String to parse. - * @is_delete: True if it is a delete request. + * @name: Name of namespace to find. + * @len:  Length of @name.   * - * Returns 0 on success, negative value otherwise. + * Returns pointer to "struct tomoyo_policy_namespace" if found, + * NULL otherwise.   *   * Caller holds tomoyo_read_lock().   */ -int tomoyo_write_aggregator(char *data, const bool is_delete) +static struct tomoyo_policy_namespace *tomoyo_find_namespace +(const char *name, const unsigned int len)  { -	char *cp = strchr(data, ' '); +	struct tomoyo_policy_namespace *ns; +	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { +		if (strncmp(name, ns->name, len) || +		    (name[len] && name[len] != ' ')) +			continue; +		return ns; +	} +	return NULL; +} -	if (!cp) -		return -EINVAL; -	*cp++ = '\0'; -	return tomoyo_update_aggregator_entry(data, cp, is_delete); +/** + * tomoyo_assign_namespace - Create a new namespace. + * + * @domainname: Name of namespace to create. + * + * Returns pointer to "struct tomoyo_policy_namespace" on success, + * NULL otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) +{ +	struct tomoyo_policy_namespace *ptr; +	struct tomoyo_policy_namespace *entry; +	const char *cp = domainname; +	unsigned int len = 0; +	while (*cp && *cp++ != ' ') +		len++; +	ptr = tomoyo_find_namespace(domainname, len); +	if (ptr) +		return ptr; +	if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname)) +		return NULL; +	entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS); +	if (!entry) +		return NULL; +	if (mutex_lock_interruptible(&tomoyo_policy_lock)) +		goto out; +	ptr = tomoyo_find_namespace(domainname, len); +	if (!ptr && tomoyo_memory_ok(entry)) { +		char *name = (char *) (entry + 1); +		ptr = entry; +		memmove(name, domainname, len); +		name[len] = '\0'; +		entry->name = name; +		tomoyo_init_policy_namespace(entry); +		entry = NULL; +	} +	mutex_unlock(&tomoyo_policy_lock); +out: +	kfree(entry); +	return ptr; +} + +/** + * tomoyo_namespace_jump - Check for namespace jump. + * + * @domainname: Name of domain. + * + * Returns true if namespace differs, false otherwise. + */ +static bool tomoyo_namespace_jump(const char *domainname) +{ +	const char *namespace = tomoyo_current_namespace()->name; +	const int len = strlen(namespace); +	return strncmp(domainname, namespace, len) || +		(domainname[len] && domainname[len] != ' ');  }  /** - * tomoyo_assign_domain - Create a domain. + * tomoyo_assign_domain - Create a domain or a namespace.   *   * @domainname: The name of domain. - * @profile:    Profile number to assign if the domain was newly created. + * @transit:    True if transit to domain found or created.   *   * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.   *   * Caller holds tomoyo_read_lock().   */  struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, -						const u8 profile) +						const bool transit)  { -	struct tomoyo_domain_info *entry; -	struct tomoyo_domain_info *domain = NULL; -	const struct tomoyo_path_info *saved_domainname; -	bool found = false; - -	if (!tomoyo_correct_domain(domainname)) +	struct tomoyo_domain_info e = { }; +	struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname); +	bool created = false; +	if (entry) { +		if (transit) { +			/* +			 * Since namespace is created at runtime, profiles may +			 * not be created by the moment the process transits to +			 * that domain. Do not perform domain transition if +			 * profile for that domain is not yet created. +			 */ +			if (tomoyo_policy_loaded && +			    !entry->ns->profile_ptr[entry->profile]) +				return NULL; +		} +		return entry; +	} +	/* Requested domain does not exist. */ +	/* Don't create requested domain if domainname is invalid. */ +	if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 || +	    !tomoyo_correct_domain(domainname)) +		return NULL; +	/* +	 * Since definition of profiles and acl_groups may differ across +	 * namespaces, do not inherit "use_profile" and "use_group" settings +	 * by automatically creating requested domain upon domain transition. +	 */ +	if (transit && tomoyo_namespace_jump(domainname)) +		return NULL; +	e.ns = tomoyo_assign_namespace(domainname); +	if (!e.ns)  		return NULL; -	saved_domainname = tomoyo_get_name(domainname); -	if (!saved_domainname) +	/* +	 * "use_profile" and "use_group" settings for automatically created +	 * domains are inherited from current domain. These are 0 for manually +	 * created domains. +	 */ +	if (transit) { +		const struct tomoyo_domain_info *domain = tomoyo_domain(); +		e.profile = domain->profile; +		e.group = domain->group; +	} +	e.domainname = tomoyo_get_name(domainname); +	if (!e.domainname)  		return NULL; -	entry = kzalloc(sizeof(*entry), GFP_NOFS);  	if (mutex_lock_interruptible(&tomoyo_policy_lock))  		goto out; -	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { -		if (domain->is_deleted || -		    tomoyo_pathcmp(saved_domainname, domain->domainname)) -			continue; -		found = true; -		break; -	} -	if (!found && tomoyo_memory_ok(entry)) { -		INIT_LIST_HEAD(&entry->acl_info_list); -		entry->domainname = saved_domainname; -		saved_domainname = NULL; -		entry->profile = profile; -		list_add_tail_rcu(&entry->list, &tomoyo_domain_list); -		domain = entry; -		entry = NULL; -		found = true; +	entry = tomoyo_find_domain(domainname); +	if (!entry) { +		entry = tomoyo_commit_ok(&e, sizeof(e)); +		if (entry) { +			INIT_LIST_HEAD(&entry->acl_info_list); +			list_add_tail_rcu(&entry->list, &tomoyo_domain_list); +			created = true; +		}  	}  	mutex_unlock(&tomoyo_policy_lock); - out: -	tomoyo_put_name(saved_domainname); -	kfree(entry); -	return found ? domain : NULL; +out: +	tomoyo_put_name(e.domainname); +	if (entry && transit) { +		if (created) { +			struct tomoyo_request_info r; +			tomoyo_init_request_info(&r, entry, +						 TOMOYO_MAC_FILE_EXECUTE); +			r.granted = false; +			tomoyo_write_log(&r, "use_profile %u\n", +					 entry->profile); +			tomoyo_write_log(&r, "use_group %u\n", entry->group); +			tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); +		} +	} +	return entry; +} + +/** + * tomoyo_environ - Check permission for environment variable names. + * + * @ee: Pointer to "struct tomoyo_execve". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_environ(struct tomoyo_execve *ee) +{ +	struct tomoyo_request_info *r = &ee->r; +	struct linux_binprm *bprm = ee->bprm; +	/* env_page.data is allocated by tomoyo_dump_page(). */ +	struct tomoyo_page_dump env_page = { }; +	char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */ +	int arg_len = 0; +	unsigned long pos = bprm->p; +	int offset = pos % PAGE_SIZE; +	int argv_count = bprm->argc; +	int envp_count = bprm->envc; +	int error = -ENOMEM; + +	ee->r.type = TOMOYO_MAC_ENVIRON; +	ee->r.profile = r->domain->profile; +	ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile, +				     TOMOYO_MAC_ENVIRON); +	if (!r->mode || !envp_count) +		return 0; +	arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); +	if (!arg_ptr) +		goto out; +	while (error == -ENOMEM) { +		if (!tomoyo_dump_page(bprm, pos, &env_page)) +			goto out; +		pos += PAGE_SIZE - offset; +		/* Read. */ +		while (argv_count && offset < PAGE_SIZE) { +			if (!env_page.data[offset++]) +				argv_count--; +		} +		if (argv_count) { +			offset = 0; +			continue; +		} +		while (offset < PAGE_SIZE) { +			const unsigned char c = env_page.data[offset++]; + +			if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) { +				if (c == '=') { +					arg_ptr[arg_len++] = '\0'; +				} else if (c == '\\') { +					arg_ptr[arg_len++] = '\\'; +					arg_ptr[arg_len++] = '\\'; +				} else if (c > ' ' && c < 127) { +					arg_ptr[arg_len++] = c; +				} else { +					arg_ptr[arg_len++] = '\\'; +					arg_ptr[arg_len++] = (c >> 6) + '0'; +					arg_ptr[arg_len++] +						= ((c >> 3) & 7) + '0'; +					arg_ptr[arg_len++] = (c & 7) + '0'; +				} +			} else { +				arg_ptr[arg_len] = '\0'; +			} +			if (c) +				continue; +			if (tomoyo_env_perm(r, arg_ptr)) { +				error = -EPERM; +				break; +			} +			if (!--envp_count) { +				error = 0; +				break; +			} +			arg_len = 0; +		} +		offset = 0; +	} +out: +	if (r->mode != TOMOYO_CONFIG_ENFORCING) +		error = 0; +	kfree(env_page.data); +	kfree(arg_ptr); +	return error;  }  /** @@ -413,54 +675,54 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,   */  int tomoyo_find_next_domain(struct linux_binprm *bprm)  { -	struct tomoyo_request_info r; -	char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);  	struct tomoyo_domain_info *old_domain = tomoyo_domain();  	struct tomoyo_domain_info *domain = NULL;  	const char *original_name = bprm->filename; -	u8 mode; -	bool is_enforce;  	int retval = -ENOMEM; -	bool need_kfree = false; -	struct tomoyo_path_info rn = { }; /* real name */ - -	mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); -	is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); -	if (!tmp) -		goto out; +	bool reject_on_transition_failure = false; +	const struct tomoyo_path_info *candidate; +	struct tomoyo_path_info exename; +	struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); - retry: -	if (need_kfree) { -		kfree(rn.name); -		need_kfree = false; +	if (!ee) +		return -ENOMEM; +	ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); +	if (!ee->tmp) { +		kfree(ee); +		return -ENOMEM;  	} +	/* ee->dump->data is allocated by tomoyo_dump_page(). */ +	tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE); +	ee->r.ee = ee; +	ee->bprm = bprm; +	ee->r.obj = &ee->obj; +	ee->obj.path1 = bprm->file->f_path;  	/* Get symlink's pathname of program. */  	retval = -ENOENT; -	rn.name = tomoyo_realpath_nofollow(original_name); -	if (!rn.name) +	exename.name = tomoyo_realpath_nofollow(original_name); +	if (!exename.name)  		goto out; -	tomoyo_fill_path_info(&rn); -	need_kfree = true; - +	tomoyo_fill_path_info(&exename); +retry:  	/* Check 'aggregator' directive. */  	{  		struct tomoyo_aggregator *ptr; -		list_for_each_entry_rcu(ptr, &tomoyo_policy_list -					[TOMOYO_ID_AGGREGATOR], head.list) { +		struct list_head *list = +			&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; +		/* Check 'aggregator' directive. */ +		candidate = &exename; +		list_for_each_entry_rcu(ptr, list, head.list) {  			if (ptr->head.is_deleted || -			    !tomoyo_path_matches_pattern(&rn, +			    !tomoyo_path_matches_pattern(&exename,  							 ptr->original_name))  				continue; -			kfree(rn.name); -			need_kfree = false; -			/* This is OK because it is read only. */ -			rn = *ptr->aggregated_name; +			candidate = ptr->aggregated_name;  			break;  		}  	}  	/* Check execute permission. */ -	retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); +	retval = tomoyo_execute_permission(&ee->r, candidate);  	if (retval == TOMOYO_RETRY_REQUEST)  		goto retry;  	if (retval < 0) @@ -471,22 +733,65 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)  	 * wildcard) rather than the pathname passed to execve()  	 * (which never contains wildcard).  	 */ -	if (r.param.path.matched_path) { -		if (need_kfree) -			kfree(rn.name); -		need_kfree = false; -		/* This is OK because it is read only. */ -		rn = *r.param.path.matched_path; -	} +	if (ee->r.param.path.matched_path) +		candidate = ee->r.param.path.matched_path; -	/* Calculate domain to transit to. */ -	switch (tomoyo_transition_type(old_domain->domainname, &rn)) { +	/* +	 * Check for domain transition preference if "file execute" matched. +	 * If preference is given, make do_execve() fail if domain transition +	 * has failed, for domain transition preference should be used with +	 * destination domain defined. +	 */ +	if (ee->transition) { +		const char *domainname = ee->transition->name; +		reject_on_transition_failure = true; +		if (!strcmp(domainname, "keep")) +			goto force_keep_domain; +		if (!strcmp(domainname, "child")) +			goto force_child_domain; +		if (!strcmp(domainname, "reset")) +			goto force_reset_domain; +		if (!strcmp(domainname, "initialize")) +			goto force_initialize_domain; +		if (!strcmp(domainname, "parent")) { +			char *cp; +			strncpy(ee->tmp, old_domain->domainname->name, +				TOMOYO_EXEC_TMPSIZE - 1); +			cp = strrchr(ee->tmp, ' '); +			if (cp) +				*cp = '\0'; +		} else if (*domainname == '<') +			strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1); +		else +			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", +				 old_domain->domainname->name, domainname); +		goto force_jump_domain; +	} +	/* +	 * No domain transition preference specified. +	 * Calculate domain to transit to. +	 */ +	switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, +				       candidate)) { +	case TOMOYO_TRANSITION_CONTROL_RESET: +force_reset_domain: +		/* Transit to the root of specified namespace. */ +		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", +			 candidate->name); +		/* +		 * Make do_execve() fail if domain transition across namespaces +		 * has failed. +		 */ +		reject_on_transition_failure = true; +		break;  	case TOMOYO_TRANSITION_CONTROL_INITIALIZE: -		/* Transit to the child of tomoyo_kernel_domain domain. */ -		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " -			 "%s", rn.name); +force_initialize_domain: +		/* Transit to the child of current namespace's root. */ +		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", +			 old_domain->ns->name, candidate->name);  		break;  	case TOMOYO_TRANSITION_CONTROL_KEEP: +force_keep_domain:  		/* Keep current domain. */  		domain = old_domain;  		break; @@ -500,43 +805,97 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)  			 * before /sbin/init.  			 */  			domain = old_domain; -		} else { -			/* Normal domain transition. */ -			snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", -				 old_domain->domainname->name, rn.name); +			break;  		} +force_child_domain: +		/* Normal domain transition. */ +		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", +			 old_domain->domainname->name, candidate->name);  		break;  	} -	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) -		goto done; -	domain = tomoyo_find_domain(tmp); +force_jump_domain: +	if (!domain) +		domain = tomoyo_assign_domain(ee->tmp, true);  	if (domain) -		goto done; -	if (is_enforce) { -		int error = tomoyo_supervisor(&r, "# wants to create domain\n" -					      "%s\n", tmp); -		if (error == TOMOYO_RETRY_REQUEST) -			goto retry; -		if (error < 0) -			goto done; +		retval = 0; +	else if (reject_on_transition_failure) { +		printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", +		       ee->tmp); +		retval = -ENOMEM; +	} else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING) +		retval = -ENOMEM; +	else { +		retval = 0; +		if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) { +			old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true; +			ee->r.granted = false; +			tomoyo_write_log(&ee->r, "%s", tomoyo_dif +					 [TOMOYO_DIF_TRANSITION_FAILED]); +			printk(KERN_WARNING +			       "ERROR: Domain '%s' not defined.\n", ee->tmp); +		}  	} -	domain = tomoyo_assign_domain(tmp, old_domain->profile); - done: -	if (domain) -		goto out; -	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); -	if (is_enforce) -		retval = -EPERM; -	else -		old_domain->transition_failed = true;   out:  	if (!domain)  		domain = old_domain;  	/* Update reference count on "struct tomoyo_domain_info". */  	atomic_inc(&domain->users);  	bprm->cred->security = domain; -	if (need_kfree) -		kfree(rn.name); -	kfree(tmp); +	kfree(exename.name); +	if (!retval) { +		ee->r.domain = domain; +		retval = tomoyo_environ(ee); +	} +	kfree(ee->tmp); +	kfree(ee->dump.data); +	kfree(ee);  	return retval;  } + +/** + * tomoyo_dump_page - Dump a page to buffer. + * + * @bprm: Pointer to "struct linux_binprm". + * @pos:  Location to dump. + * @dump: Poiner to "struct tomoyo_page_dump". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, +		      struct tomoyo_page_dump *dump) +{ +	struct page *page; + +	/* dump->data is released by tomoyo_find_next_domain(). */ +	if (!dump->data) { +		dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); +		if (!dump->data) +			return false; +	} +	/* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ +#ifdef CONFIG_MMU +	if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) +		return false; +#else +	page = bprm->page[pos / PAGE_SIZE]; +#endif +	if (page != dump->page) { +		const unsigned int offset = pos % PAGE_SIZE; +		/* +		 * Maybe kmap()/kunmap() should be used here. +		 * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). +		 * So do I. +		 */ +		char *kaddr = kmap_atomic(page); + +		dump->page = page; +		memcpy(dump->data + offset, kaddr + offset, +		       PAGE_SIZE - offset); +		kunmap_atomic(kaddr); +	} +	/* Same with put_arg_page(page) in fs/exec.c */ +#ifdef CONFIG_MMU +	put_page(page); +#endif +	return true; +} diff --git a/security/tomoyo/environ.c b/security/tomoyo/environ.c new file mode 100644 index 00000000000..ad4c6e18a43 --- /dev/null +++ b/security/tomoyo/environ.c @@ -0,0 +1,122 @@ +/* + * security/tomoyo/environ.c + * + * Copyright (C) 2005-2011  NTT DATA CORPORATION + */ + +#include "common.h" + +/** + * tomoyo_check_env_acl - Check permission for environment variable's name. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_env_acl(struct tomoyo_request_info *r, +				 const struct tomoyo_acl_info *ptr) +{ +	const struct tomoyo_env_acl *acl = +		container_of(ptr, typeof(*acl), head); + +	return tomoyo_path_matches_pattern(r->param.environ.name, acl->env); +} + +/** + * tomoyo_audit_env_log - Audit environment variable name log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_env_log(struct tomoyo_request_info *r) +{ +	return tomoyo_supervisor(r, "misc env %s\n", +				 r->param.environ.name->name); +} + +/** + * tomoyo_env_perm - Check permission for environment variable's name. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @env: The name of environment variable. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env) +{ +	struct tomoyo_path_info environ; +	int error; + +	if (!env || !*env) +		return 0; +	environ.name = env; +	tomoyo_fill_path_info(&environ); +	r->param_type = TOMOYO_TYPE_ENV_ACL; +	r->param.environ.name = &environ; +	do { +		tomoyo_check_acl(r, tomoyo_check_env_acl); +		error = tomoyo_audit_env_log(r); +	} while (error == TOMOYO_RETRY_REQUEST); +	return error; +} + +/** + * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a, +				const struct tomoyo_acl_info *b) +{ +	const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head); +	const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head); + +	return p1->env == p2->env; +} + +/** + * tomoyo_write_env - Write "struct tomoyo_env_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_env(struct tomoyo_acl_param *param) +{ +	struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL }; +	int error = -ENOMEM; +	const char *data = tomoyo_read_token(param); + +	if (!tomoyo_correct_word(data) || strchr(data, '=')) +		return -EINVAL; +	e.env = tomoyo_get_name(data); +	if (!e.env) +		return error; +	error = tomoyo_update_domain(&e.head, sizeof(e), param, +				  tomoyo_same_env_acl, NULL); +	tomoyo_put_name(e.env); +	return error; +} + +/** + * tomoyo_write_misc - Update environment variable list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_misc(struct tomoyo_acl_param *param) +{ +	if (tomoyo_str_starts(¶m->data, "env ")) +		return tomoyo_write_env(param); +	return -EINVAL; +} diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 9d32f182301..40039079074 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -1,80 +1,51 @@  /*   * security/tomoyo/file.c   * - * Pathname restriction functions. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include "common.h"  #include <linux/slab.h> -/* Keyword array for operations with one pathname. */ -const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { -	[TOMOYO_TYPE_READ_WRITE] = "read/write", -	[TOMOYO_TYPE_EXECUTE]    = "execute", -	[TOMOYO_TYPE_READ]       = "read", -	[TOMOYO_TYPE_WRITE]      = "write", -	[TOMOYO_TYPE_UNLINK]     = "unlink", -	[TOMOYO_TYPE_RMDIR]      = "rmdir", -	[TOMOYO_TYPE_TRUNCATE]   = "truncate", -	[TOMOYO_TYPE_SYMLINK]    = "symlink", -	[TOMOYO_TYPE_REWRITE]    = "rewrite", -	[TOMOYO_TYPE_CHROOT]     = "chroot", -	[TOMOYO_TYPE_UMOUNT]     = "unmount", -}; - -/* Keyword array for operations with one pathname and three numbers. */ -const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = { -	[TOMOYO_TYPE_MKBLOCK]    = "mkblock", -	[TOMOYO_TYPE_MKCHAR]     = "mkchar", -}; - -/* Keyword array for operations with two pathnames. */ -const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { -	[TOMOYO_TYPE_LINK]       = "link", -	[TOMOYO_TYPE_RENAME]     = "rename", -	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", -}; - -/* Keyword array for operations with one pathname and one number. */ -const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { -	[TOMOYO_TYPE_CREATE]     = "create", -	[TOMOYO_TYPE_MKDIR]      = "mkdir", -	[TOMOYO_TYPE_MKFIFO]     = "mkfifo", -	[TOMOYO_TYPE_MKSOCK]     = "mksock", -	[TOMOYO_TYPE_IOCTL]      = "ioctl", -	[TOMOYO_TYPE_CHMOD]      = "chmod", -	[TOMOYO_TYPE_CHOWN]      = "chown", -	[TOMOYO_TYPE_CHGRP]      = "chgrp", -}; - +/* + * Mapping table from "enum tomoyo_path_acl_index" to "enum tomoyo_mac_index". + */  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_APPEND]     = TOMOYO_MAC_FILE_OPEN,  	[TOMOYO_TYPE_UNLINK]     = TOMOYO_MAC_FILE_UNLINK, +	[TOMOYO_TYPE_GETATTR]    = TOMOYO_MAC_FILE_GETATTR,  	[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_MKDEV_OPERATION] = { +/* + * Mapping table from "enum tomoyo_mkdev_acl_index" to "enum tomoyo_mac_index". + */ +const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {  	[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,  	[TOMOYO_TYPE_MKCHAR]  = TOMOYO_MAC_FILE_MKCHAR,  }; -static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { +/* + * Mapping table from "enum tomoyo_path2_acl_index" to "enum tomoyo_mac_index". + */ +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] = { +/* + * Mapping table from "enum tomoyo_path_number_acl_index" to + * "enum tomoyo_mac_index". + */ +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, @@ -85,41 +56,76 @@ static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {  	[TOMOYO_TYPE_CHGRP]  = TOMOYO_MAC_FILE_CHGRP,  }; +/** + * tomoyo_put_name_union - Drop reference on "struct tomoyo_name_union". + * + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns nothing. + */  void tomoyo_put_name_union(struct tomoyo_name_union *ptr)  { -	if (!ptr) -		return; -	if (ptr->is_group) -		tomoyo_put_group(ptr->group); -	else -		tomoyo_put_name(ptr->filename); +	tomoyo_put_group(ptr->group); +	tomoyo_put_name(ptr->filename);  } +/** + * tomoyo_compare_name_union - Check whether a name matches "struct tomoyo_name_union" or not. + * + * @name: Pointer to "struct tomoyo_path_info". + * @ptr:  Pointer to "struct tomoyo_name_union". + * + * Returns "struct tomoyo_path_info" if @name matches @ptr, NULL otherwise. + */  const struct tomoyo_path_info *  tomoyo_compare_name_union(const struct tomoyo_path_info *name,  			  const struct tomoyo_name_union *ptr)  { -	if (ptr->is_group) +	if (ptr->group)  		return tomoyo_path_matches_group(name, ptr->group);  	if (tomoyo_path_matches_pattern(name, ptr->filename))  		return ptr->filename;  	return NULL;  } +/** + * tomoyo_put_number_union - Drop reference on "struct tomoyo_number_union". + * + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns nothing. + */  void tomoyo_put_number_union(struct tomoyo_number_union *ptr)  { -	if (ptr && ptr->is_group) -		tomoyo_put_group(ptr->group); +	tomoyo_put_group(ptr->group);  } +/** + * tomoyo_compare_number_union - Check whether a value matches "struct tomoyo_number_union" or not. + * + * @value: Number to check. + * @ptr:   Pointer to "struct tomoyo_number_union". + * + * Returns true if @value matches @ptr, false otherwise. + */  bool tomoyo_compare_number_union(const unsigned long value,  				 const struct tomoyo_number_union *ptr)  { -	if (ptr->is_group) +	if (ptr->group)  		return tomoyo_number_matches_group(value, value, ptr->group);  	return value >= ptr->values[0] && value <= ptr->values[1];  } +/** + * tomoyo_add_slash - Add trailing '/' if needed. + * + * @buf: Pointer to "struct tomoyo_path_info". + * + * Returns nothing. + * + * @buf must be generated by tomoyo_encode() because this function does not + * allocate memory for adding '/'. + */  static void tomoyo_add_slash(struct tomoyo_path_info *buf)  {  	if (buf->is_dir) @@ -132,24 +138,6 @@ static void tomoyo_add_slash(struct tomoyo_path_info *buf)  }  /** - * tomoyo_strendswith - Check whether the token ends with the given token. - * - * @name: The token to check. - * @tail: The token to find. - * - * Returns true if @name ends with @tail, false otherwise. - */ -static bool tomoyo_strendswith(const char *name, const char *tail) -{ -	int len; - -	if (!name || !tail) -		return false; -	len = strlen(name) - strlen(tail); -	return len >= 0 && !strcmp(name + len, tail); -} - -/**   * tomoyo_get_realpath - Get realpath.   *   * @buf:  Pointer to "struct tomoyo_path_info". @@ -164,7 +152,7 @@ static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)  		tomoyo_fill_path_info(buf);  		return true;  	} -        return false; +	return false;  }  /** @@ -176,13 +164,9 @@ static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)   */  static int tomoyo_audit_path_log(struct tomoyo_request_info *r)  { -	const char *operation = tomoyo_path_keyword[r->param.path.operation]; -	const struct tomoyo_path_info *filename = r->param.path.filename; -	if (r->granted) -		return 0; -	tomoyo_warn_log(r, "%s %s", operation, filename->name); -	return tomoyo_supervisor(r, "allow_%s %s\n", operation, -				 tomoyo_pattern(filename)); +	return tomoyo_supervisor(r, "file %s %s\n", tomoyo_path_keyword +				 [r->param.path.operation], +				 r->param.path.filename->name);  }  /** @@ -194,16 +178,10 @@ static int tomoyo_audit_path_log(struct tomoyo_request_info *r)   */  static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)  { -	const char *operation = tomoyo_path2_keyword[r->param.path2.operation]; -	const struct tomoyo_path_info *filename1 = r->param.path2.filename1; -	const struct tomoyo_path_info *filename2 = r->param.path2.filename2; -	if (r->granted) -		return 0; -	tomoyo_warn_log(r, "%s %s %s", operation, filename1->name, -			filename2->name); -	return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, -				 tomoyo_pattern(filename1), -				 tomoyo_pattern(filename2)); +	return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords +				 [tomoyo_pp2mac[r->param.path2.operation]], +				 r->param.path2.filename1->name, +				 r->param.path2.filename2->name);  }  /** @@ -215,24 +193,18 @@ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)   */  static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r)  { -	const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation]; -	const struct tomoyo_path_info *filename = r->param.mkdev.filename; -	const unsigned int major = r->param.mkdev.major; -	const unsigned int minor = r->param.mkdev.minor; -	const unsigned int mode = r->param.mkdev.mode; -	if (r->granted) -		return 0; -	tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode, -			major, minor); -	return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation, -				 tomoyo_pattern(filename), mode, major, minor); +	return tomoyo_supervisor(r, "file %s %s 0%o %u %u\n", +				 tomoyo_mac_keywords +				 [tomoyo_pnnn2mac[r->param.mkdev.operation]], +				 r->param.mkdev.filename->name, +				 r->param.mkdev.mode, r->param.mkdev.major, +				 r->param.mkdev.minor);  }  /**   * tomoyo_audit_path_number_log - Audit path/number request log.   * - * @r:     Pointer to "struct tomoyo_request_info". - * @error: Error code. + * @r: Pointer to "struct tomoyo_request_info".   *   * Returns 0 on success, negative value otherwise.   */ @@ -240,11 +212,7 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)  {  	const u8 type = r->param.path_number.operation;  	u8 radix; -	const struct tomoyo_path_info *filename = r->param.path_number.filename; -	const char *operation = tomoyo_path_number_keyword[type];  	char buffer[64]; -	if (r->granted) -		return 0;  	switch (type) {  	case TOMOYO_TYPE_CREATE:  	case TOMOYO_TYPE_MKDIR: @@ -262,251 +230,23 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)  	}  	tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number,  			   radix); -	tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer); -	return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, -				 tomoyo_pattern(filename), buffer); -} - -static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, -					  const struct tomoyo_acl_head *b) -{ -	return container_of(a, struct tomoyo_readable_file, -			    head)->filename == -		container_of(b, struct tomoyo_readable_file, -			     head)->filename; -} - -/** - * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list. - * - * @filename:  Filename unconditionally permitted to open() for reading. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_update_globally_readable_entry(const char *filename, -						 const bool is_delete) -{ -	struct tomoyo_readable_file e = { }; -	int error; - -	if (!tomoyo_correct_word(filename)) -		return -EINVAL; -	e.filename = tomoyo_get_name(filename); -	if (!e.filename) -		return -ENOMEM; -	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -				     &tomoyo_policy_list -				     [TOMOYO_ID_GLOBALLY_READABLE], -				     tomoyo_same_globally_readable); -	tomoyo_put_name(e.filename); -	return error; -} - -/** - * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. - * - * @filename: The filename to check. - * - * Returns true if any domain can open @filename for reading, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * -					     filename) -{ -	struct tomoyo_readable_file *ptr; -	bool found = false; - -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list -				[TOMOYO_ID_GLOBALLY_READABLE], head.list) { -		if (!ptr->head.is_deleted && -		    tomoyo_path_matches_pattern(filename, ptr->filename)) { -			found = true; -			break; -		} -	} -	return found; -} - -/** - * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list. - * - * @data:      String to parse. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_globally_readable(char *data, const bool is_delete) -{ -	return tomoyo_update_globally_readable_entry(data, is_delete); -} - -static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, -				const struct tomoyo_acl_head *b) -{ -	return container_of(a, struct tomoyo_no_pattern, head)->pattern == -		container_of(b, struct tomoyo_no_pattern, head)->pattern; -} - -/** - * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list. - * - * @pattern:   Pathname pattern. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_update_file_pattern_entry(const char *pattern, -					    const bool is_delete) -{ -	struct tomoyo_no_pattern e = { }; -	int error; - -	if (!tomoyo_correct_word(pattern)) -		return -EINVAL; -	e.pattern = tomoyo_get_name(pattern); -	if (!e.pattern) -		return -ENOMEM; -	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -				     &tomoyo_policy_list[TOMOYO_ID_PATTERN], -				     tomoyo_same_pattern); -	tomoyo_put_name(e.pattern); -	return error; -} - -/** - * tomoyo_pattern - Get patterned pathname. - * - * @filename: The filename to find patterned pathname. - * - * Returns pointer to pathname pattern if matched, @filename otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -const char *tomoyo_pattern(const struct tomoyo_path_info *filename) -{ -	struct tomoyo_no_pattern *ptr; -	const struct tomoyo_path_info *pattern = NULL; - -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN], -				head.list) { -		if (ptr->head.is_deleted) -			continue; -		if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) -			continue; -		pattern = ptr->pattern; -		if (tomoyo_strendswith(pattern->name, "/\\*")) { -			/* Do nothing. Try to find the better match. */ -		} else { -			/* This would be the better match. Use this. */ -			break; -		} -	} -	if (pattern) -		filename = pattern; -	return filename->name; -} - -/** - * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list. - * - * @data:      String to parse. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_pattern(char *data, const bool is_delete) -{ -	return tomoyo_update_file_pattern_entry(data, is_delete); -} - -static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, -				   const struct tomoyo_acl_head *b) -{ -	return container_of(a, struct tomoyo_no_rewrite, head)->pattern -		== container_of(b, struct tomoyo_no_rewrite, head) -		->pattern; -} - -/** - * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list. - * - * @pattern:   Pathname pattern that are not rewritable by default. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_update_no_rewrite_entry(const char *pattern, -					  const bool is_delete) -{ -	struct tomoyo_no_rewrite e = { }; -	int error; - -	if (!tomoyo_correct_word(pattern)) -		return -EINVAL; -	e.pattern = tomoyo_get_name(pattern); -	if (!e.pattern) -		return -ENOMEM; -	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -				     &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], -				     tomoyo_same_no_rewrite); -	tomoyo_put_name(e.pattern); -	return error; -} - -/** - * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. - * - * @filename: Filename to check. - * - * Returns true if @filename is specified by "deny_rewrite" directive, - * false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) -{ -	struct tomoyo_no_rewrite *ptr; -	bool found = false; - -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], -				head.list) { -		if (ptr->head.is_deleted) -			continue; -		if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) -			continue; -		found = true; -		break; -	} -	return found; +	return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords +				 [tomoyo_pn2mac[type]], +				 r->param.path_number.filename->name, buffer);  }  /** - * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list. + * tomoyo_check_path_acl - Check permission for path operation.   * - * @data:      String to parse. - * @is_delete: True if it is a delete request. + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info".   * - * Returns 0 on success, negative value otherwise. + * Returns true if granted, false otherwise.   * - * Caller holds tomoyo_read_lock(). + * To be able to use wildcard for domain transition, this function sets + * matching entry on success. Since the caller holds tomoyo_read_lock(), + * it is safe to set matching entry.   */ -int tomoyo_write_no_rewrite(char *data, const bool is_delete) -{ -	return tomoyo_update_no_rewrite_entry(data, is_delete); -} -  static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,  				  const struct tomoyo_acl_info *ptr)  { @@ -521,6 +261,14 @@ static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,  	return false;  } +/** + * tomoyo_check_path_number_acl - Check permission for path number operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */  static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,  					 const struct tomoyo_acl_info *ptr)  { @@ -533,6 +281,14 @@ static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,  					  &acl->name);  } +/** + * tomoyo_check_path2_acl - Check permission for path path operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */  static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,  				   const struct tomoyo_acl_info *ptr)  { @@ -544,8 +300,16 @@ static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,  					     &acl->name2);  } +/** + * tomoyo_check_mkdev_acl - Check permission for path number number number operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */  static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, -				const struct tomoyo_acl_info *ptr) +				   const struct tomoyo_acl_info *ptr)  {  	const struct tomoyo_mkdev_acl *acl =  		container_of(ptr, typeof(*acl), head); @@ -560,15 +324,31 @@ static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,  					  &acl->name);  } +/** + * tomoyo_same_path_acl - Check for duplicated "struct tomoyo_path_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */  static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,  				 const struct tomoyo_acl_info *b)  {  	const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);  	const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); -	return tomoyo_same_acl_head(&p1->head, &p2->head) && -		tomoyo_same_name_union(&p1->name, &p2->name); +	return tomoyo_same_name_union(&p1->name, &p2->name);  } +/** + * tomoyo_merge_path_acl - Merge duplicated "struct tomoyo_path_acl" entry. + * + * @a:         Pointer to "struct tomoyo_acl_info". + * @b:         Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */  static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,  				  struct tomoyo_acl_info *b,  				  const bool is_delete) @@ -577,19 +357,10 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,  		->perm;  	u16 perm = *a_perm;  	const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; -	if (is_delete) { +	if (is_delete)  		perm &= ~b_perm; -		if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK) -			perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); -		else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE))) -			perm &= ~TOMOYO_RW_MASK; -	} else { +	else  		perm |= b_perm; -		if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK) -			perm |= (1 << TOMOYO_TYPE_READ_WRITE); -		else if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) -			perm |= TOMOYO_RW_MASK; -	}  	*a_perm = perm;  	return !perm;  } @@ -597,52 +368,62 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,  /**   * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.   * - * @type:      Type of operation. - * @filename:  Filename. - * @domain:    Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm:  Permission. + * @param: Pointer to "struct tomoyo_acl_param".   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_update_path_acl(const u8 type, const char *filename, -				  struct tomoyo_domain_info * const domain, -				  const bool is_delete) +static int tomoyo_update_path_acl(const u16 perm, +				  struct tomoyo_acl_param *param)  {  	struct tomoyo_path_acl e = {  		.head.type = TOMOYO_TYPE_PATH_ACL, -		.perm = 1 << type +		.perm = perm  	};  	int error; -	if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE)) -		e.perm |= TOMOYO_RW_MASK; -	if (!tomoyo_parse_name_union(filename, &e.name)) -		return -EINVAL; -	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, -				     tomoyo_same_path_acl, -				     tomoyo_merge_path_acl); +	if (!tomoyo_parse_name_union(param, &e.name)) +		error = -EINVAL; +	else +		error = tomoyo_update_domain(&e.head, sizeof(e), param, +					     tomoyo_same_path_acl, +					     tomoyo_merge_path_acl);  	tomoyo_put_name_union(&e.name);  	return error;  } +/** + * tomoyo_same_mkdev_acl - Check for duplicated "struct tomoyo_mkdev_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */  static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,  					 const struct tomoyo_acl_info *b)  { -	const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), -								head); -	const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), -								head); -	return tomoyo_same_acl_head(&p1->head, &p2->head) -		&& tomoyo_same_name_union(&p1->name, &p2->name) -		&& tomoyo_same_number_union(&p1->mode, &p2->mode) -		&& tomoyo_same_number_union(&p1->major, &p2->major) -		&& tomoyo_same_number_union(&p1->minor, &p2->minor); +	const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head); +	const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head); +	return tomoyo_same_name_union(&p1->name, &p2->name) && +		tomoyo_same_number_union(&p1->mode, &p2->mode) && +		tomoyo_same_number_union(&p1->major, &p2->major) && +		tomoyo_same_number_union(&p1->minor, &p2->minor);  } +/** + * tomoyo_merge_mkdev_acl - Merge duplicated "struct tomoyo_mkdev_acl" entry. + * + * @a:         Pointer to "struct tomoyo_acl_info". + * @b:         Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */  static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, -					  struct tomoyo_acl_info *b, -					  const bool is_delete) +				   struct tomoyo_acl_info *b, +				   const bool is_delete)  {  	u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl,  					 head)->perm; @@ -660,37 +441,30 @@ static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,  /**   * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list.   * - * @type:      Type of operation. - * @filename:  Filename. - * @mode:      Create mode. - * @major:     Device major number. - * @minor:     Device minor number. - * @domain:    Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm:  Permission. + * @param: Pointer to "struct tomoyo_acl_param".   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_update_mkdev_acl(const u8 type, const char *filename, -					  char *mode, char *major, char *minor, -					  struct tomoyo_domain_info * const -					  domain, const bool is_delete) +static int tomoyo_update_mkdev_acl(const u8 perm, +				   struct tomoyo_acl_param *param)  {  	struct tomoyo_mkdev_acl e = {  		.head.type = TOMOYO_TYPE_MKDEV_ACL, -		.perm = 1 << type +		.perm = perm  	}; -	int error = is_delete ? -ENOENT : -ENOMEM; -	if (!tomoyo_parse_name_union(filename, &e.name) || -	    !tomoyo_parse_number_union(mode, &e.mode) || -	    !tomoyo_parse_number_union(major, &e.major) || -	    !tomoyo_parse_number_union(minor, &e.minor)) -		goto out; -	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, -				     tomoyo_same_mkdev_acl, -				     tomoyo_merge_mkdev_acl); - out: +	int error; +	if (!tomoyo_parse_name_union(param, &e.name) || +	    !tomoyo_parse_number_union(param, &e.mode) || +	    !tomoyo_parse_number_union(param, &e.major) || +	    !tomoyo_parse_number_union(param, &e.minor)) +		error = -EINVAL; +	else +		error = tomoyo_update_domain(&e.head, sizeof(e), param, +					     tomoyo_same_mkdev_acl, +					     tomoyo_merge_mkdev_acl);  	tomoyo_put_name_union(&e.name);  	tomoyo_put_number_union(&e.mode);  	tomoyo_put_number_union(&e.major); @@ -698,16 +472,32 @@ static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,  	return error;  } +/** + * tomoyo_same_path2_acl - Check for duplicated "struct tomoyo_path2_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */  static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,  				  const struct tomoyo_acl_info *b)  {  	const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);  	const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); -	return tomoyo_same_acl_head(&p1->head, &p2->head) -		&& tomoyo_same_name_union(&p1->name1, &p2->name1) -		&& tomoyo_same_name_union(&p1->name2, &p2->name2); +	return tomoyo_same_name_union(&p1->name1, &p2->name1) && +		tomoyo_same_name_union(&p1->name2, &p2->name2);  } +/** + * tomoyo_merge_path2_acl - Merge duplicated "struct tomoyo_path2_acl" entry. + * + * @a:         Pointer to "struct tomoyo_acl_info". + * @b:         Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */  static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,  				   struct tomoyo_acl_info *b,  				   const bool is_delete) @@ -727,33 +517,28 @@ static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,  /**   * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.   * - * @type:      Type of operation. - * @filename1: First filename. - * @filename2: Second filename. - * @domain:    Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm:  Permission. + * @param: Pointer to "struct tomoyo_acl_param".   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_update_path2_acl(const u8 type, const char *filename1, -				   const char *filename2, -				   struct tomoyo_domain_info * const domain, -				   const bool is_delete) +static int tomoyo_update_path2_acl(const u8 perm, +				   struct tomoyo_acl_param *param)  {  	struct tomoyo_path2_acl e = {  		.head.type = TOMOYO_TYPE_PATH2_ACL, -		.perm = 1 << type +		.perm = perm  	}; -	int error = is_delete ? -ENOENT : -ENOMEM; -	if (!tomoyo_parse_name_union(filename1, &e.name1) || -	    !tomoyo_parse_name_union(filename2, &e.name2)) -		goto out; -	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, -				     tomoyo_same_path2_acl, -				     tomoyo_merge_path2_acl); - out: +	int error; +	if (!tomoyo_parse_name_union(param, &e.name1) || +	    !tomoyo_parse_name_union(param, &e.name2)) +		error = -EINVAL; +	else +		error = tomoyo_update_domain(&e.head, sizeof(e), param, +					     tomoyo_same_path2_acl, +					     tomoyo_merge_path2_acl);  	tomoyo_put_name_union(&e.name1);  	tomoyo_put_name_union(&e.name2);  	return error; @@ -770,14 +555,13 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1,   *   * Caller holds tomoyo_read_lock().   */ -int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, -			   const struct tomoyo_path_info *filename) +static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, +				  const struct tomoyo_path_info *filename)  {  	int error; - next:  	r->type = tomoyo_p2mac[operation]; -	r->mode = tomoyo_get_mode(r->profile, r->type); +	r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);  	if (r->mode == TOMOYO_CONFIG_DISABLED)  		return 0;  	r->param_type = TOMOYO_TYPE_PATH_ACL; @@ -785,30 +569,50 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,  	r->param.path.operation = operation;  	do {  		tomoyo_check_acl(r, tomoyo_check_path_acl); -		if (!r->granted && operation == TOMOYO_TYPE_READ && -		    !r->domain->ignore_global_allow_read && -		    tomoyo_globally_readable_file(filename)) -			r->granted = true;  		error = tomoyo_audit_path_log(r); -		/* -		 * Do not retry for execute request, for alias may have -		 * changed. -		 */ -	} while (error == TOMOYO_RETRY_REQUEST && -		 operation != TOMOYO_TYPE_EXECUTE); +	} while (error == TOMOYO_RETRY_REQUEST); +	return error; +} + +/** + * tomoyo_execute_permission - Check permission for execute operation. + * + * @r:         Pointer to "struct tomoyo_request_info". + * @filename:  Filename to check. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_execute_permission(struct tomoyo_request_info *r, +			      const struct tomoyo_path_info *filename) +{  	/* -	 * Since "allow_truncate" doesn't imply "allow_rewrite" permission, -	 * we need to check "allow_rewrite" permission if the filename is -	 * specified by "deny_rewrite" keyword. +	 * Unlike other permission checks, this check is done regardless of +	 * profile mode settings in order to check for domain transition +	 * preference.  	 */ -	if (!error && operation == TOMOYO_TYPE_TRUNCATE && -	    tomoyo_no_rewrite_file(filename)) { -		operation = TOMOYO_TYPE_REWRITE; -		goto next; -	} -	return error; +	r->type = TOMOYO_MAC_FILE_EXECUTE; +	r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type); +	r->param_type = TOMOYO_TYPE_PATH_ACL; +	r->param.path.filename = filename; +	r->param.path.operation = TOMOYO_TYPE_EXECUTE; +	tomoyo_check_acl(r, tomoyo_check_path_acl); +	r->ee->transition = r->matched_acl && r->matched_acl->cond ? +		r->matched_acl->cond->transit : NULL; +	if (r->mode != TOMOYO_CONFIG_DISABLED) +		return tomoyo_audit_path_log(r); +	return 0;  } +/** + * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */  static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,  					const struct tomoyo_acl_info *b)  { @@ -816,11 +620,19 @@ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,  							       head);  	const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),  							       head); -	return tomoyo_same_acl_head(&p1->head, &p2->head) -		&& tomoyo_same_name_union(&p1->name, &p2->name) -		&& tomoyo_same_number_union(&p1->number, &p2->number); +	return tomoyo_same_name_union(&p1->name, &p2->name) && +		tomoyo_same_number_union(&p1->number, &p2->number);  } +/** + * tomoyo_merge_path_number_acl - Merge duplicated "struct tomoyo_path_number_acl" entry. + * + * @a:         Pointer to "struct tomoyo_acl_info". + * @b:         Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */  static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,  					 struct tomoyo_acl_info *b,  					 const bool is_delete) @@ -841,33 +653,26 @@ static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,  /**   * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.   * - * @type:      Type of operation. - * @filename:  Filename. - * @number:    Number. - * @domain:    Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm:  Permission. + * @param: Pointer to "struct tomoyo_acl_param".   *   * Returns 0 on success, negative value otherwise.   */ -static int tomoyo_update_path_number_acl(const u8 type, const char *filename, -					 char *number, -					 struct tomoyo_domain_info * const -					 domain, -					 const bool is_delete) +static int tomoyo_update_path_number_acl(const u8 perm, +					 struct tomoyo_acl_param *param)  {  	struct tomoyo_path_number_acl e = {  		.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, -		.perm = 1 << type +		.perm = perm  	}; -	int error = is_delete ? -ENOENT : -ENOMEM; -	if (!tomoyo_parse_name_union(filename, &e.name)) -		return -EINVAL; -	if (!tomoyo_parse_number_union(number, &e.number)) -		goto out; -	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, -				     tomoyo_same_path_number_acl, -				     tomoyo_merge_path_number_acl); - out: +	int error; +	if (!tomoyo_parse_name_union(param, &e.name) || +	    !tomoyo_parse_number_union(param, &e.number)) +		error = -EINVAL; +	else +		error = tomoyo_update_domain(&e.head, sizeof(e), param, +					     tomoyo_same_path_number_acl, +					     tomoyo_merge_path_number_acl);  	tomoyo_put_name_union(&e.name);  	tomoyo_put_number_union(&e.number);  	return error; @@ -886,16 +691,20 @@ int tomoyo_path_number_perm(const u8 type, struct path *path,  			    unsigned long number)  {  	struct tomoyo_request_info r; +	struct tomoyo_obj_info obj = { +		.path1 = *path, +	};  	int error = -ENOMEM;  	struct tomoyo_path_info buf;  	int idx;  	if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) -	    == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry) +	    == TOMOYO_CONFIG_DISABLED || !path->dentry)  		return 0;  	idx = tomoyo_read_lock();  	if (!tomoyo_get_realpath(&buf, path))  		goto out; +	r.obj = &obj;  	if (type == TOMOYO_TYPE_MKDIR)  		tomoyo_add_slash(&buf);  	r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; @@ -927,51 +736,33 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,  				 struct path *path, const int flag)  {  	const u8 acc_mode = ACC_MODE(flag); -	int error = -ENOMEM; +	int error = 0;  	struct tomoyo_path_info buf;  	struct tomoyo_request_info r; +	struct tomoyo_obj_info obj = { +		.path1 = *path, +	};  	int idx; -	if (!path->mnt || -	    (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))) -		return 0;  	buf.name = NULL;  	r.mode = TOMOYO_CONFIG_DISABLED;  	idx = tomoyo_read_lock(); -	if (!tomoyo_get_realpath(&buf, path)) -		goto out; -	error = 0; -	/* -	 * If the filename is specified by "deny_rewrite" keyword, -	 * 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_APPEND) -	    && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE) +	if (acc_mode && +	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)  	    != TOMOYO_CONFIG_DISABLED) {  		if (!tomoyo_get_realpath(&buf, path)) {  			error = -ENOMEM;  			goto out;  		} -		if (tomoyo_no_rewrite_file(&buf)) -			error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, +		r.obj = &obj; +		if (acc_mode & MAY_READ) +			error = tomoyo_path_permission(&r, TOMOYO_TYPE_READ, +						       &buf); +		if (!error && (acc_mode & MAY_WRITE)) +			error = tomoyo_path_permission(&r, (flag & O_APPEND) ? +						       TOMOYO_TYPE_APPEND : +						       TOMOYO_TYPE_WRITE,  						       &buf); -	} -	if (!error && acc_mode && -	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) -	    != TOMOYO_CONFIG_DISABLED) { -		u8 operation; -		if (!buf.name && !tomoyo_get_realpath(&buf, path)) { -			error = -ENOMEM; -			goto out; -		} -		if (acc_mode == (MAY_READ | MAY_WRITE)) -			operation = TOMOYO_TYPE_READ_WRITE; -		else if (acc_mode == MAY_READ) -			operation = TOMOYO_TYPE_READ; -		else -			operation = TOMOYO_TYPE_WRITE; -		error = tomoyo_path_permission(&r, operation, &buf);  	}   out:  	kfree(buf.name); @@ -982,47 +773,57 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,  }  /** - * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount". + * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount".   *   * @operation: Type of operation.   * @path:      Pointer to "struct path". + * @target:    Symlink's target if @operation is TOMOYO_TYPE_SYMLINK, + *             NULL otherwise.   *   * Returns 0 on success, negative value otherwise.   */ -int tomoyo_path_perm(const u8 operation, struct path *path) +int tomoyo_path_perm(const u8 operation, struct path *path, const char *target)  { -	int error = -ENOMEM; -	struct tomoyo_path_info buf;  	struct tomoyo_request_info r; +	struct tomoyo_obj_info obj = { +		.path1 = *path, +	}; +	int error; +	struct tomoyo_path_info buf; +	bool is_enforce; +	struct tomoyo_path_info symlink_target;  	int idx; -	if (!path->mnt) -		return 0;  	if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])  	    == TOMOYO_CONFIG_DISABLED)  		return 0; +	is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING); +	error = -ENOMEM;  	buf.name = NULL;  	idx = tomoyo_read_lock();  	if (!tomoyo_get_realpath(&buf, path))  		goto out; +	r.obj = &obj;  	switch (operation) { -	case TOMOYO_TYPE_REWRITE: -		if (!tomoyo_no_rewrite_file(&buf)) { -			error = 0; -			goto out; -		} -		break;  	case TOMOYO_TYPE_RMDIR:  	case TOMOYO_TYPE_CHROOT: -	case TOMOYO_TYPE_UMOUNT:  		tomoyo_add_slash(&buf);  		break; +	case TOMOYO_TYPE_SYMLINK: +		symlink_target.name = tomoyo_encode(target); +		if (!symlink_target.name) +			goto out; +		tomoyo_fill_path_info(&symlink_target); +		obj.symlink_target = &symlink_target; +		break;  	}  	error = tomoyo_path_permission(&r, operation, &buf); +	if (operation == TOMOYO_TYPE_SYMLINK) +		kfree(symlink_target.name);   out:  	kfree(buf.name);  	tomoyo_read_unlock(idx); -	if (r.mode != TOMOYO_CONFIG_ENFORCING) +	if (!is_enforce)  		error = 0;  	return error;  } @@ -1038,20 +839,23 @@ int tomoyo_path_perm(const u8 operation, struct path *path)   * Returns 0 on success, negative value otherwise.   */  int tomoyo_mkdev_perm(const u8 operation, struct path *path, -			     const unsigned int mode, unsigned int dev) +		      const unsigned int mode, unsigned int dev)  {  	struct tomoyo_request_info r; +	struct tomoyo_obj_info obj = { +		.path1 = *path, +	};  	int error = -ENOMEM;  	struct tomoyo_path_info buf;  	int idx; -	if (!path->mnt || -	    tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation]) +	if (tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])  	    == TOMOYO_CONFIG_DISABLED)  		return 0;  	idx = tomoyo_read_lock();  	error = -ENOMEM;  	if (tomoyo_get_realpath(&buf, path)) { +		r.obj = &obj;  		dev = new_decode_dev(dev);  		r.param_type = TOMOYO_TYPE_MKDEV_ACL;  		r.param.mkdev.filename = &buf; @@ -1085,10 +889,13 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,  	struct tomoyo_path_info buf1;  	struct tomoyo_path_info buf2;  	struct tomoyo_request_info r; +	struct tomoyo_obj_info obj = { +		.path1 = *path1, +		.path2 = *path2, +	};  	int idx; -	if (!path1->mnt || !path2->mnt || -	    tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) +	if (tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])  	    == TOMOYO_CONFIG_DISABLED)  		return 0;  	buf1.name = NULL; @@ -1100,16 +907,17 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,  	switch (operation) {  		struct dentry *dentry;  	case TOMOYO_TYPE_RENAME: -        case TOMOYO_TYPE_LINK: +	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); +		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; -        } +	} +	r.obj = &obj;  	r.param_type = TOMOYO_TYPE_PATH2_ACL;  	r.param.path2.operation = operation;  	r.param.path2.filename1 = &buf1; @@ -1128,53 +936,91 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,  }  /** + * tomoyo_same_mount_acl - Check for duplicated "struct tomoyo_mount_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, +				  const struct tomoyo_acl_info *b) +{ +	const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); +	const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); +	return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && +		tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && +		tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && +		tomoyo_same_number_union(&p1->flags, &p2->flags); +} + +/** + * tomoyo_update_mount_acl - Write "struct tomoyo_mount_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param) +{ +	struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; +	int error; +	if (!tomoyo_parse_name_union(param, &e.dev_name) || +	    !tomoyo_parse_name_union(param, &e.dir_name) || +	    !tomoyo_parse_name_union(param, &e.fs_type) || +	    !tomoyo_parse_number_union(param, &e.flags)) +		error = -EINVAL; +	else +		error = tomoyo_update_domain(&e.head, sizeof(e), param, +					     tomoyo_same_mount_acl, NULL); +	tomoyo_put_name_union(&e.dev_name); +	tomoyo_put_name_union(&e.dir_name); +	tomoyo_put_name_union(&e.fs_type); +	tomoyo_put_number_union(&e.flags); +	return error; +} + +/**   * tomoyo_write_file - Update file related list.   * - * @data:      String to parse. - * @domain:    Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @param: Pointer to "struct tomoyo_acl_param".   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, -		      const bool is_delete) +int tomoyo_write_file(struct tomoyo_acl_param *param)  { -	char *w[5]; +	u16 perm = 0;  	u8 type; -	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) -		return -EINVAL; -	if (strncmp(w[0], "allow_", 6)) -		goto out; -	w[0] += 6; -	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { -		if (strcmp(w[0], tomoyo_path_keyword[type])) -			continue; -		return tomoyo_update_path_acl(type, w[1], domain, is_delete); -	} -	if (!w[2][0]) -		goto out; -	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { -		if (strcmp(w[0], tomoyo_path2_keyword[type])) -			continue; -		return tomoyo_update_path2_acl(type, w[1], w[2], domain, -					       is_delete); -	} -	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) { -		if (strcmp(w[0], tomoyo_path_number_keyword[type])) -			continue; -		return tomoyo_update_path_number_acl(type, w[1], w[2], domain, -						     is_delete); -	} -	if (!w[3][0] || !w[4][0]) -		goto out; -	for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) { -		if (strcmp(w[0], tomoyo_mkdev_keyword[type])) -			continue; -		return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3], -					       w[4], domain, is_delete); -	} - out: +	const char *operation = tomoyo_read_token(param); +	for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) +		if (tomoyo_permstr(operation, tomoyo_path_keyword[type])) +			perm |= 1 << type; +	if (perm) +		return tomoyo_update_path_acl(perm, param); +	for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) +		if (tomoyo_permstr(operation, +				   tomoyo_mac_keywords[tomoyo_pp2mac[type]])) +			perm |= 1 << type; +	if (perm) +		return tomoyo_update_path2_acl(perm, param); +	for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) +		if (tomoyo_permstr(operation, +				   tomoyo_mac_keywords[tomoyo_pn2mac[type]])) +			perm |= 1 << type; +	if (perm) +		return tomoyo_update_path_number_acl(perm, param); +	for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) +		if (tomoyo_permstr(operation, +				   tomoyo_mac_keywords[tomoyo_pnnn2mac[type]])) +			perm |= 1 << type; +	if (perm) +		return tomoyo_update_mkdev_acl(perm, param); +	if (tomoyo_permstr(operation, +			   tomoyo_mac_keywords[TOMOYO_MAC_FILE_MOUNT])) +		return tomoyo_update_mount_acl(param);  	return -EINVAL;  } diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index a877e4c3b10..986a6a75686 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -1,59 +1,109 @@  /*   * security/tomoyo/gc.c   * - * Implementation of the Domain-Based Mandatory Access Control. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION - * + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include "common.h"  #include <linux/kthread.h>  #include <linux/slab.h> -struct tomoyo_gc { -	struct list_head list; -	int type; -	struct list_head *element; -}; -static LIST_HEAD(tomoyo_gc_queue); -static DEFINE_MUTEX(tomoyo_gc_mutex); - -/* Caller holds tomoyo_policy_lock mutex. */ -static bool tomoyo_add_to_gc(const int type, struct list_head *element) +/** + * tomoyo_memory_free - Free memory for elements. + * + * @ptr:  Pointer to allocated memory. + * + * Returns nothing. + * + * Caller holds tomoyo_policy_lock mutex. + */ +static inline void tomoyo_memory_free(void *ptr)  { -	struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); -	if (!entry) -		return false; -	entry->type = type; -	entry->element = element; -	list_add(&entry->list, &tomoyo_gc_queue); -	list_del_rcu(element); -	return true; +	tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr); +	kfree(ptr);  } -static void tomoyo_del_allow_read(struct list_head *element) -{ -	struct tomoyo_readable_file *ptr = -		container_of(element, typeof(*ptr), head.list); -	tomoyo_put_name(ptr->filename); -} +/* The list for "struct tomoyo_io_buffer". */ +static LIST_HEAD(tomoyo_io_buffer_list); +/* Lock for protecting tomoyo_io_buffer_list. */ +static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); -static void tomoyo_del_file_pattern(struct list_head *element) +/** + * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. + * + * @element: Pointer to "struct list_head". + * + * Returns true if @element is used by /sys/kernel/security/tomoyo/ users, + * false otherwise. + */ +static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)  { -	struct tomoyo_no_pattern *ptr = -		container_of(element, typeof(*ptr), head.list); -	tomoyo_put_name(ptr->pattern); +	struct tomoyo_io_buffer *head; +	bool in_use = false; + +	spin_lock(&tomoyo_io_buffer_list_lock); +	list_for_each_entry(head, &tomoyo_io_buffer_list, list) { +		head->users++; +		spin_unlock(&tomoyo_io_buffer_list_lock); +		mutex_lock(&head->io_sem); +		if (head->r.domain == element || head->r.group == element || +		    head->r.acl == element || &head->w.domain->list == element) +			in_use = true; +		mutex_unlock(&head->io_sem); +		spin_lock(&tomoyo_io_buffer_list_lock); +		head->users--; +		if (in_use) +			break; +	} +	spin_unlock(&tomoyo_io_buffer_list_lock); +	return in_use;  } -static void tomoyo_del_no_rewrite(struct list_head *element) +/** + * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. + * + * @string: String to check. + * + * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, + * false otherwise. + */ +static bool tomoyo_name_used_by_io_buffer(const char *string)  { -	struct tomoyo_no_rewrite *ptr = -		container_of(element, typeof(*ptr), head.list); -	tomoyo_put_name(ptr->pattern); +	struct tomoyo_io_buffer *head; +	const size_t size = strlen(string) + 1; +	bool in_use = false; + +	spin_lock(&tomoyo_io_buffer_list_lock); +	list_for_each_entry(head, &tomoyo_io_buffer_list, list) { +		int i; +		head->users++; +		spin_unlock(&tomoyo_io_buffer_list_lock); +		mutex_lock(&head->io_sem); +		for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { +			const char *w = head->r.w[i]; +			if (w < string || w > string + size) +				continue; +			in_use = true; +			break; +		} +		mutex_unlock(&head->io_sem); +		spin_lock(&tomoyo_io_buffer_list_lock); +		head->users--; +		if (in_use) +			break; +	} +	spin_unlock(&tomoyo_io_buffer_list_lock); +	return in_use;  } -static void tomoyo_del_transition_control(struct list_head *element) +/** + * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_transition_control(struct list_head *element)  {  	struct tomoyo_transition_control *ptr =  		container_of(element, typeof(*ptr), head.list); @@ -61,7 +111,14 @@ static void tomoyo_del_transition_control(struct list_head *element)  	tomoyo_put_name(ptr->program);  } -static void tomoyo_del_aggregator(struct list_head *element) +/** + * tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_aggregator(struct list_head *element)  {  	struct tomoyo_aggregator *ptr =  		container_of(element, typeof(*ptr), head.list); @@ -69,17 +126,32 @@ static void tomoyo_del_aggregator(struct list_head *element)  	tomoyo_put_name(ptr->aggregated_name);  } -static void tomoyo_del_manager(struct list_head *element) +/** + * tomoyo_del_manager - Delete members in "struct tomoyo_manager". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_manager(struct list_head *element)  {  	struct tomoyo_manager *ptr =  		container_of(element, typeof(*ptr), head.list);  	tomoyo_put_name(ptr->manager);  } +/** + * tomoyo_del_acl - Delete members in "struct tomoyo_acl_info". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */  static void tomoyo_del_acl(struct list_head *element)  {  	struct tomoyo_acl_info *acl =  		container_of(element, typeof(*acl), list); +	tomoyo_put_condition(acl->cond);  	switch (acl->type) {  	case TOMOYO_TYPE_PATH_ACL:  		{ @@ -124,231 +196,460 @@ static void tomoyo_del_acl(struct list_head *element)  			tomoyo_put_number_union(&entry->flags);  		}  		break; +	case TOMOYO_TYPE_ENV_ACL: +		{ +			struct tomoyo_env_acl *entry = +				container_of(acl, typeof(*entry), head); + +			tomoyo_put_name(entry->env); +		} +		break; +	case TOMOYO_TYPE_INET_ACL: +		{ +			struct tomoyo_inet_acl *entry = +				container_of(acl, typeof(*entry), head); + +			tomoyo_put_group(entry->address.group); +			tomoyo_put_number_union(&entry->port); +		} +		break; +	case TOMOYO_TYPE_UNIX_ACL: +		{ +			struct tomoyo_unix_acl *entry = +				container_of(acl, typeof(*entry), head); + +			tomoyo_put_name_union(&entry->name); +		} +		break; +	case TOMOYO_TYPE_MANUAL_TASK_ACL: +		{ +			struct tomoyo_task_acl *entry = +				container_of(acl, typeof(*entry), head); +			tomoyo_put_name(entry->domainname); +		} +		break;  	}  } -static bool tomoyo_del_domain(struct list_head *element) +/** + * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + * + * Caller holds tomoyo_policy_lock mutex. + */ +static inline void tomoyo_del_domain(struct list_head *element)  {  	struct tomoyo_domain_info *domain =  		container_of(element, typeof(*domain), list);  	struct tomoyo_acl_info *acl;  	struct tomoyo_acl_info *tmp;  	/* -	 * Since we don't protect whole execve() operation using SRCU, -	 * we need to recheck domain->users at this point. -	 * -	 * (1) Reader starts SRCU section upon execve(). -	 * (2) Reader traverses tomoyo_domain_list and finds this domain. -	 * (3) Writer marks this domain as deleted. -	 * (4) Garbage collector removes this domain from tomoyo_domain_list -	 *     because this domain is marked as deleted and used by nobody. -	 * (5) Reader saves reference to this domain into -	 *     "struct linux_binprm"->cred->security . -	 * (6) Reader finishes SRCU section, although execve() operation has -	 *     not finished yet. -	 * (7) Garbage collector waits for SRCU synchronization. -	 * (8) Garbage collector kfree() this domain because this domain is -	 *     used by nobody. -	 * (9) Reader finishes execve() operation and restores this domain from -	 *     "struct linux_binprm"->cred->security. -	 * -	 * By updating domain->users at (5), we can solve this race problem -	 * by rechecking domain->users at (8). +	 * Since this domain is referenced from neither +	 * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete +	 * elements without checking for is_deleted flag.  	 */ -	if (atomic_read(&domain->users)) -		return false;  	list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {  		tomoyo_del_acl(&acl->list);  		tomoyo_memory_free(acl);  	}  	tomoyo_put_name(domain->domainname); -	return true;  } +/** + * tomoyo_del_condition - Delete members in "struct tomoyo_condition". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +void tomoyo_del_condition(struct list_head *element) +{ +	struct tomoyo_condition *cond = container_of(element, typeof(*cond), +						     head.list); +	const u16 condc = cond->condc; +	const u16 numbers_count = cond->numbers_count; +	const u16 names_count = cond->names_count; +	const u16 argc = cond->argc; +	const u16 envc = cond->envc; +	unsigned int i; +	const struct tomoyo_condition_element *condp +		= (const struct tomoyo_condition_element *) (cond + 1); +	struct tomoyo_number_union *numbers_p +		= (struct tomoyo_number_union *) (condp + condc); +	struct tomoyo_name_union *names_p +		= (struct tomoyo_name_union *) (numbers_p + numbers_count); +	const struct tomoyo_argv *argv +		= (const struct tomoyo_argv *) (names_p + names_count); +	const struct tomoyo_envp *envp +		= (const struct tomoyo_envp *) (argv + argc); +	for (i = 0; i < numbers_count; i++) +		tomoyo_put_number_union(numbers_p++); +	for (i = 0; i < names_count; i++) +		tomoyo_put_name_union(names_p++); +	for (i = 0; i < argc; argv++, i++) +		tomoyo_put_name(argv->value); +	for (i = 0; i < envc; envp++, i++) { +		tomoyo_put_name(envp->name); +		tomoyo_put_name(envp->value); +	} +} -static void tomoyo_del_name(struct list_head *element) +/** + * tomoyo_del_name - Delete members in "struct tomoyo_name". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_name(struct list_head *element)  { -	const struct tomoyo_name *ptr = -		container_of(element, typeof(*ptr), list); +	/* Nothing to do. */  } -static void tomoyo_del_path_group(struct list_head *element) +/** + * tomoyo_del_path_group - Delete members in "struct tomoyo_path_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_path_group(struct list_head *element)  {  	struct tomoyo_path_group *member =  		container_of(element, typeof(*member), head.list);  	tomoyo_put_name(member->member_name);  } -static void tomoyo_del_group(struct list_head *element) +/** + * tomoyo_del_group - Delete "struct tomoyo_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_group(struct list_head *element)  {  	struct tomoyo_group *group = -		container_of(element, typeof(*group), list); +		container_of(element, typeof(*group), head.list);  	tomoyo_put_name(group->group_name);  } -static void tomoyo_del_number_group(struct list_head *element) +/** + * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_address_group(struct list_head *element)  { -	struct tomoyo_number_group *member = -		container_of(element, typeof(*member), head.list); +	/* Nothing to do. */  } -static bool tomoyo_collect_member(struct list_head *member_list, int id) +/** + * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_number_group(struct list_head *element) +{ +	/* Nothing to do. */ +} + +/** + * tomoyo_try_to_gc - Try to kfree() an entry. + * + * @type:    One of values in "enum tomoyo_policy_id". + * @element: Pointer to "struct list_head". + * + * Returns nothing. + * + * Caller holds tomoyo_policy_lock mutex. + */ +static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, +			     struct list_head *element) +{ +	/* +	 * __list_del_entry() guarantees that the list element became no longer +	 * reachable from the list which the element was originally on (e.g. +	 * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the +	 * list element became no longer referenced by syscall users. +	 */ +	__list_del_entry(element); +	mutex_unlock(&tomoyo_policy_lock); +	synchronize_srcu(&tomoyo_ss); +	/* +	 * However, there are two users which may still be using the list +	 * element. We need to defer until both users forget this element. +	 * +	 * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl} +	 * and "struct tomoyo_io_buffer"->w.domain forget this element. +	 */ +	if (tomoyo_struct_used_by_io_buffer(element)) +		goto reinject; +	switch (type) { +	case TOMOYO_ID_TRANSITION_CONTROL: +		tomoyo_del_transition_control(element); +		break; +	case TOMOYO_ID_MANAGER: +		tomoyo_del_manager(element); +		break; +	case TOMOYO_ID_AGGREGATOR: +		tomoyo_del_aggregator(element); +		break; +	case TOMOYO_ID_GROUP: +		tomoyo_del_group(element); +		break; +	case TOMOYO_ID_PATH_GROUP: +		tomoyo_del_path_group(element); +		break; +	case TOMOYO_ID_ADDRESS_GROUP: +		tomoyo_del_address_group(element); +		break; +	case TOMOYO_ID_NUMBER_GROUP: +		tomoyo_del_number_group(element); +		break; +	case TOMOYO_ID_CONDITION: +		tomoyo_del_condition(element); +		break; +	case TOMOYO_ID_NAME: +		/* +		 * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[] +		 * forget this element. +		 */ +		if (tomoyo_name_used_by_io_buffer +		    (container_of(element, typeof(struct tomoyo_name), +				  head.list)->entry.name)) +			goto reinject; +		tomoyo_del_name(element); +		break; +	case TOMOYO_ID_ACL: +		tomoyo_del_acl(element); +		break; +	case TOMOYO_ID_DOMAIN: +		/* +		 * Don't kfree() until all "struct cred"->security forget this +		 * element. +		 */ +		if (atomic_read(&container_of +				(element, typeof(struct tomoyo_domain_info), +				 list)->users)) +			goto reinject; +		break; +	case TOMOYO_MAX_POLICY: +		break; +	} +	mutex_lock(&tomoyo_policy_lock); +	if (type == TOMOYO_ID_DOMAIN) +		tomoyo_del_domain(element); +	tomoyo_memory_free(element); +	return; +reinject: +	/* +	 * We can safely reinject this element here bacause +	 * (1) Appending list elements and removing list elements are protected +	 *     by tomoyo_policy_lock mutex. +	 * (2) Only this function removes list elements and this function is +	 *     exclusively executed by tomoyo_gc_mutex mutex. +	 * are true. +	 */ +	mutex_lock(&tomoyo_policy_lock); +	list_add_rcu(element, element->prev); +} + +/** + * tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head". + * + * @id:          One of values in "enum tomoyo_policy_id". + * @member_list: Pointer to "struct list_head". + * + * Returns nothing. + */ +static void tomoyo_collect_member(const enum tomoyo_policy_id id, +				  struct list_head *member_list)  {  	struct tomoyo_acl_head *member; -	list_for_each_entry(member, member_list, list) { +	struct tomoyo_acl_head *tmp; +	list_for_each_entry_safe(member, tmp, member_list, list) {  		if (!member->is_deleted)  			continue; -		if (!tomoyo_add_to_gc(id, &member->list)) -			return false; +		member->is_deleted = TOMOYO_GC_IN_PROGRESS; +		tomoyo_try_to_gc(id, &member->list);  	} -        return true;  } -static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) +/** + * tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info". + * + * @list: Pointer to "struct list_head". + * + * Returns nothing. + */ +static void tomoyo_collect_acl(struct list_head *list)  {  	struct tomoyo_acl_info *acl; -	list_for_each_entry(acl, &domain->acl_info_list, list) { +	struct tomoyo_acl_info *tmp; +	list_for_each_entry_safe(acl, tmp, list, list) {  		if (!acl->is_deleted)  			continue; -		if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) -			return false; +		acl->is_deleted = TOMOYO_GC_IN_PROGRESS; +		tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list);  	} -	return true;  } +/** + * tomoyo_collect_entry - Try to kfree() deleted elements. + * + * Returns nothing. + */  static void tomoyo_collect_entry(void)  {  	int i; -	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		return; -	for (i = 0; i < TOMOYO_MAX_POLICY; i++) { -		if (!tomoyo_collect_member(&tomoyo_policy_list[i], i)) -			goto unlock; -	} +	enum tomoyo_policy_id id; +	struct tomoyo_policy_namespace *ns; +	mutex_lock(&tomoyo_policy_lock);  	{  		struct tomoyo_domain_info *domain; -		list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { -			if (!tomoyo_collect_acl(domain)) -				goto unlock; +		struct tomoyo_domain_info *tmp; +		list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, +					 list) { +			tomoyo_collect_acl(&domain->acl_info_list);  			if (!domain->is_deleted || atomic_read(&domain->users))  				continue; -			/* -			 * Nobody is referring this domain. But somebody may -			 * refer this domain after successful execve(). -			 * We recheck domain->users after SRCU synchronization. -			 */ -			if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) -				goto unlock; +			tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list);  		}  	} -	for (i = 0; i < TOMOYO_MAX_HASH; i++) { -		struct tomoyo_name *ptr; -		list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { -			if (atomic_read(&ptr->users)) +	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { +		for (id = 0; id < TOMOYO_MAX_POLICY; id++) +			tomoyo_collect_member(id, &ns->policy_list[id]); +		for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) +			tomoyo_collect_acl(&ns->acl_group[i]); +	} +	{ +		struct tomoyo_shared_acl_head *ptr; +		struct tomoyo_shared_acl_head *tmp; +		list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, +					 list) { +			if (atomic_read(&ptr->users) > 0)  				continue; -			if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) -				goto unlock; +			atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); +			tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list);  		}  	} -	for (i = 0; i < TOMOYO_MAX_GROUP; i++) { -		struct list_head *list = &tomoyo_group_list[i]; -		int id; -		struct tomoyo_group *group; -		switch (i) { -		case 0: -			id = TOMOYO_ID_PATH_GROUP; -			break; -		default: -			id = TOMOYO_ID_NUMBER_GROUP; -			break; +	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { +		for (i = 0; i < TOMOYO_MAX_GROUP; i++) { +			struct list_head *list = &ns->group_list[i]; +			struct tomoyo_group *group; +			struct tomoyo_group *tmp; +			switch (i) { +			case 0: +				id = TOMOYO_ID_PATH_GROUP; +				break; +			case 1: +				id = TOMOYO_ID_NUMBER_GROUP; +				break; +			default: +				id = TOMOYO_ID_ADDRESS_GROUP; +				break; +			} +			list_for_each_entry_safe(group, tmp, list, head.list) { +				tomoyo_collect_member(id, &group->member_list); +				if (!list_empty(&group->member_list) || +				    atomic_read(&group->head.users) > 0) +					continue; +				atomic_set(&group->head.users, +					   TOMOYO_GC_IN_PROGRESS); +				tomoyo_try_to_gc(TOMOYO_ID_GROUP, +						 &group->head.list); +			}  		} -		list_for_each_entry(group, list, list) { -			if (!tomoyo_collect_member(&group->member_list, id)) -				goto unlock; -			if (!list_empty(&group->member_list) || -			    atomic_read(&group->users)) +	} +	for (i = 0; i < TOMOYO_MAX_HASH; i++) { +		struct list_head *list = &tomoyo_name_list[i]; +		struct tomoyo_shared_acl_head *ptr; +		struct tomoyo_shared_acl_head *tmp; +		list_for_each_entry_safe(ptr, tmp, list, list) { +			if (atomic_read(&ptr->users) > 0)  				continue; -			if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list)) -				goto unlock; +			atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); +			tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list);  		}  	} - unlock:  	mutex_unlock(&tomoyo_policy_lock);  } -static void tomoyo_kfree_entry(void) +/** + * tomoyo_gc_thread - Garbage collector thread function. + * + * @unused: Unused. + * + * Returns 0. + */ +static int tomoyo_gc_thread(void *unused)  { -	struct tomoyo_gc *p; -	struct tomoyo_gc *tmp; +	/* Garbage collector thread is exclusive. */ +	static DEFINE_MUTEX(tomoyo_gc_mutex); +	if (!mutex_trylock(&tomoyo_gc_mutex)) +		goto out; +	tomoyo_collect_entry(); +	{ +		struct tomoyo_io_buffer *head; +		struct tomoyo_io_buffer *tmp; -	list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { -		struct list_head *element = p->element; -		switch (p->type) { -		case TOMOYO_ID_TRANSITION_CONTROL: -			tomoyo_del_transition_control(element); -			break; -		case TOMOYO_ID_AGGREGATOR: -			tomoyo_del_aggregator(element); -			break; -		case TOMOYO_ID_GLOBALLY_READABLE: -			tomoyo_del_allow_read(element); -			break; -		case TOMOYO_ID_PATTERN: -			tomoyo_del_file_pattern(element); -			break; -		case TOMOYO_ID_NO_REWRITE: -			tomoyo_del_no_rewrite(element); -			break; -		case TOMOYO_ID_MANAGER: -			tomoyo_del_manager(element); -			break; -		case TOMOYO_ID_NAME: -			tomoyo_del_name(element); -			break; -		case TOMOYO_ID_ACL: -			tomoyo_del_acl(element); -			break; -		case TOMOYO_ID_DOMAIN: -			if (!tomoyo_del_domain(element)) +		spin_lock(&tomoyo_io_buffer_list_lock); +		list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list, +					 list) { +			if (head->users)  				continue; -			break; -		case TOMOYO_ID_PATH_GROUP: -			tomoyo_del_path_group(element); -			break; -		case TOMOYO_ID_GROUP: -			tomoyo_del_group(element); -			break; -		case TOMOYO_ID_NUMBER_GROUP: -			tomoyo_del_number_group(element); -			break; +			list_del(&head->list); +			kfree(head->read_buf); +			kfree(head->write_buf); +			kfree(head);  		} -		tomoyo_memory_free(element); -		list_del(&p->list); -		kfree(p); +		spin_unlock(&tomoyo_io_buffer_list_lock);  	} +	mutex_unlock(&tomoyo_gc_mutex); +out: +	/* This acts as do_exit(0). */ +	return 0;  } -static int tomoyo_gc_thread(void *unused) +/** + * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users. + * + * @head:        Pointer to "struct tomoyo_io_buffer". + * @is_register: True if register, false if unregister. + * + * Returns nothing. + */ +void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)  { -	daemonize("GC for TOMOYO"); -	if (mutex_trylock(&tomoyo_gc_mutex)) { -		int i; -		for (i = 0; i < 10; i++) { -			tomoyo_collect_entry(); -			if (list_empty(&tomoyo_gc_queue)) -				break; -			synchronize_srcu(&tomoyo_ss); -			tomoyo_kfree_entry(); +	bool is_write = false; + +	spin_lock(&tomoyo_io_buffer_list_lock); +	if (is_register) { +		head->users = 1; +		list_add(&head->list, &tomoyo_io_buffer_list); +	} else { +		is_write = head->write_buf != NULL; +		if (!--head->users) { +			list_del(&head->list); +			kfree(head->read_buf); +			kfree(head->write_buf); +			kfree(head);  		} -		mutex_unlock(&tomoyo_gc_mutex);  	} -	do_exit(0); -} - -void tomoyo_run_gc(void) -{ -	struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, -						  "GC for TOMOYO"); -	if (!IS_ERR(task)) -		wake_up_process(task); +	spin_unlock(&tomoyo_io_buffer_list_lock); +	if (is_write) { +		struct task_struct *task = kthread_create(tomoyo_gc_thread, +							  NULL, +							  "GC for TOMOYO"); +		if (!IS_ERR(task)) +			wake_up_process(task); +	}  } diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index e94352ce723..50092534ec5 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c @@ -1,21 +1,37 @@  /*   * security/tomoyo/group.c   * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/slab.h>  #include "common.h" +/** + * tomoyo_same_path_group - Check for duplicated "struct tomoyo_path_group" 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_path_group(const struct tomoyo_acl_head *a, -				const struct tomoyo_acl_head *b) +				   const struct tomoyo_acl_head *b)  {  	return container_of(a, struct tomoyo_path_group, head)->member_name ==  		container_of(b, struct tomoyo_path_group, head)->member_name;  } +/** + * tomoyo_same_number_group - Check for duplicated "struct tomoyo_number_group" 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_number_group(const struct tomoyo_acl_head *a, -				  const struct tomoyo_acl_head *b) +				     const struct tomoyo_acl_head *b)  {  	return !memcmp(&container_of(a, struct tomoyo_number_group, head)  		       ->number, @@ -26,50 +42,70 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,  }  /** - * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. + * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry.   * - * @data:      String to parse. - * @is_delete: True if it is a delete request. - * @type:      Type of this group. + * @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_address_group(const struct tomoyo_acl_head *a, +				      const struct tomoyo_acl_head *b) +{ +	const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1), +							     head); +	const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2), +							     head); + +	return tomoyo_same_ipaddr_union(&p1->address, &p2->address); +} + +/** + * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * @type:  Type of this group.   *   * Returns 0 on success, negative value otherwise.   */ -int tomoyo_write_group(char *data, const bool is_delete, const u8 type) +int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)  { -	struct tomoyo_group *group; -	struct list_head *member; -	char *w[2]; +	struct tomoyo_group *group = tomoyo_get_group(param, type);  	int error = -EINVAL; -	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) -		return -EINVAL; -	group = tomoyo_get_group(w[0], type);  	if (!group)  		return -ENOMEM; -	member = &group->member_list; +	param->list = &group->member_list;  	if (type == TOMOYO_PATH_GROUP) {  		struct tomoyo_path_group e = { }; -		e.member_name = tomoyo_get_name(w[1]); +		e.member_name = tomoyo_get_name(tomoyo_read_token(param));  		if (!e.member_name) {  			error = -ENOMEM;  			goto out;  		} -		error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -					     member, tomoyo_same_path_group); +		error = tomoyo_update_policy(&e.head, sizeof(e), param, +					  tomoyo_same_path_group);  		tomoyo_put_name(e.member_name);  	} else if (type == TOMOYO_NUMBER_GROUP) {  		struct tomoyo_number_group e = { }; -		if (w[1][0] == '@' -		    || !tomoyo_parse_number_union(w[1], &e.number) -		    || e.number.values[0] > e.number.values[1]) +		if (param->data[0] == '@' || +		    !tomoyo_parse_number_union(param, &e.number))  			goto out; -		error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -					     member, tomoyo_same_number_group); +		error = tomoyo_update_policy(&e.head, sizeof(e), param, +					  tomoyo_same_number_group);  		/*  		 * tomoyo_put_number_union() is not needed because -		 * w[1][0] != '@'. +		 * param->data[0] != '@'.  		 */ +	} else { +		struct tomoyo_address_group e = { }; + +		if (param->data[0] == '@' || +		    !tomoyo_parse_ipaddr_union(param, &e.address)) +			goto out; +		error = tomoyo_update_policy(&e.head, sizeof(e), param, +					     tomoyo_same_address_group);  	} - out: +out:  	tomoyo_put_group(group);  	return error;  } @@ -77,8 +113,8 @@ int tomoyo_write_group(char *data, const bool is_delete, const u8 type)  /**   * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.   * - * @pathname:        The name of pathname. - * @group:           Pointer to "struct tomoyo_path_group". + * @pathname: The name of pathname. + * @group:    Pointer to "struct tomoyo_path_group".   *   * Returns matched member's pathname if @pathname matches pathnames in @group,   * NULL otherwise. @@ -128,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min,  	}  	return matched;  } + +/** + * tomoyo_address_matches_group - Check whether the given address matches members of the given address group. + * + * @is_ipv6: True if @address is an IPv6 address. + * @address: An IPv4 or IPv6 address. + * @group:   Pointer to "struct tomoyo_address_group". + * + * Returns true if @address matches addresses in @group group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, +				  const struct tomoyo_group *group) +{ +	struct tomoyo_address_group *member; +	bool matched = false; +	const u8 size = is_ipv6 ? 16 : 4; + +	list_for_each_entry_rcu(member, &group->member_list, head.list) { +		if (member->head.is_deleted) +			continue; +		if (member->address.is_ipv6 != is_ipv6) +			continue; +		if (memcmp(&member->address.ip[0], address, size) > 0 || +		    memcmp(address, &member->address.ip[1], size) > 0) +			continue; +		matched = true; +		break; +	} +	return matched; +} diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c index bbada7ca1b9..078fac0bb4c 100644 --- a/security/tomoyo/load_policy.c +++ b/security/tomoyo/load_policy.c @@ -1,15 +1,32 @@  /*   * security/tomoyo/load_policy.c   * - * Policy loader launcher for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include "common.h" -/* path to policy loader */ -static const char *tomoyo_loader = "/sbin/tomoyo-init"; +#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + +/* + * Path to the policy loader. (default = CONFIG_SECURITY_TOMOYO_POLICY_LOADER) + */ +static const char *tomoyo_loader; + +/** + * tomoyo_loader_setup - Set policy loader. + * + * @str: Program to use as a policy loader (e.g. /sbin/tomoyo-init ). + * + * Returns 0. + */ +static int __init tomoyo_loader_setup(char *str) +{ +	tomoyo_loader = str; +	return 0; +} + +__setup("TOMOYO_loader=", tomoyo_loader_setup);  /**   * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. @@ -18,24 +35,38 @@ static const char *tomoyo_loader = "/sbin/tomoyo-init";   */  static bool tomoyo_policy_loader_exists(void)  { -	/* -	 * Don't activate MAC if the policy loader doesn't exist. -	 * If the initrd includes /sbin/init but real-root-dev has not -	 * mounted on / yet, activating MAC will block the system since -	 * policies are not loaded yet. -	 * Thus, let do_execve() call this function everytime. -	 */  	struct path path; - +	if (!tomoyo_loader) +		tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;  	if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { -		printk(KERN_INFO "Not activating Mandatory Access Control now " -		       "since %s doesn't exist.\n", tomoyo_loader); +		printk(KERN_INFO "Not activating Mandatory Access Control " +		       "as %s does not exist.\n", tomoyo_loader);  		return false;  	}  	path_put(&path);  	return true;  } +/* + * Path to the trigger. (default = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER) + */ +static const char *tomoyo_trigger; + +/** + * tomoyo_trigger_setup - Set trigger for activation. + * + * @str: Program to use as an activation trigger (e.g. /sbin/init ). + * + * Returns 0. + */ +static int __init tomoyo_trigger_setup(char *str) +{ +	tomoyo_trigger = str; +	return 0; +} + +__setup("TOMOYO_trigger=", tomoyo_trigger_setup); +  /**   * tomoyo_load_policy - Run external policy loader to load policy.   * @@ -51,24 +82,19 @@ static bool tomoyo_policy_loader_exists(void)   */  void tomoyo_load_policy(const char *filename)  { +	static bool done;  	char *argv[2];  	char *envp[3]; -	if (tomoyo_policy_loaded) +	if (tomoyo_policy_loaded || done)  		return; -	/* -	 * Check filename is /sbin/init or /sbin/tomoyo-start. -	 * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't -	 * be passed. -	 * You can create /sbin/tomoyo-start by -	 * "ln -s /bin/true /sbin/tomoyo-start". -	 */ -	if (strcmp(filename, "/sbin/init") && -	    strcmp(filename, "/sbin/tomoyo-start")) +	if (!tomoyo_trigger) +		tomoyo_trigger = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER; +	if (strcmp(filename, tomoyo_trigger))  		return;  	if (!tomoyo_policy_loader_exists())  		return; - +	done = true;  	printk(KERN_INFO "Calling %s to load policy. Please wait.\n",  	       tomoyo_loader);  	argv[0] = (char *) tomoyo_loader; @@ -76,6 +102,8 @@ void tomoyo_load_policy(const char *filename)  	envp[0] = "HOME=/";  	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";  	envp[2] = NULL; -	call_usermodehelper(argv[0], argv, envp, 1); +	call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);  	tomoyo_check_profile();  } + +#endif diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 297612669c7..0e995716cc2 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -1,9 +1,7 @@  /*   * security/tomoyo/memory.c   * - * Memory management functions for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/hash.h> @@ -29,10 +27,10 @@ void tomoyo_warn_oom(const char *function)  		panic("MAC Initialization failed.\n");  } -/* Memory allocated for policy. */ -static atomic_t tomoyo_policy_memory_size; -/* Quota for holding policy. */ -static unsigned int tomoyo_quota_for_policy; +/* Memoy currently used by policy/audit log/query. */ +unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; +/* Memory quota for "policy"/"audit log"/"query". */ +unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];  /**   * tomoyo_memory_ok - Check memory quota. @@ -42,18 +40,20 @@ static unsigned int tomoyo_quota_for_policy;   * Returns true on success, false otherwise.   *   * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. + * + * Caller holds tomoyo_policy_lock mutex.   */  bool tomoyo_memory_ok(void *ptr)  { -	size_t s = ptr ? ksize(ptr) : 0; -	atomic_add(s, &tomoyo_policy_memory_size); -	if (ptr && (!tomoyo_quota_for_policy || -		    atomic_read(&tomoyo_policy_memory_size) -		    <= tomoyo_quota_for_policy)) { -		memset(ptr, 0, s); -		return true; +	if (ptr) { +		const size_t s = ksize(ptr); +		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; +		if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || +		    tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= +		    tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]) +			return true; +		tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;  	} -	atomic_sub(s, &tomoyo_policy_memory_size);  	tomoyo_warn_oom(__func__);  	return false;  } @@ -66,6 +66,8 @@ bool tomoyo_memory_ok(void *ptr)   *   * Returns pointer to allocated memory on success, NULL otherwise.   * @data is zero-cleared on success. + * + * Caller holds tomoyo_policy_lock mutex.   */  void *tomoyo_commit_ok(void *data, const unsigned int size)  { @@ -75,32 +77,25 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)  		memset(data, 0, size);  		return ptr;  	} -	return NULL; -} - -/** - * tomoyo_memory_free - Free memory for elements. - * - * @ptr:  Pointer to allocated memory. - */ -void tomoyo_memory_free(void *ptr) -{ -	atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);  	kfree(ptr); +	return NULL;  }  /**   * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".   * - * @group_name: The name of address group. - * @idx:        Index number. + * @param: Pointer to "struct tomoyo_acl_param". + * @idx:   Index number.   *   * Returns pointer to "struct tomoyo_group" on success, NULL otherwise.   */ -struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) +struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, +				      const u8 idx)  {  	struct tomoyo_group e = { };  	struct tomoyo_group *group = NULL; +	struct list_head *list; +	const char *group_name = tomoyo_read_token(param);  	bool found = false;  	if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)  		return NULL; @@ -109,10 +104,12 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)  		return NULL;  	if (mutex_lock_interruptible(&tomoyo_policy_lock))  		goto out; -	list_for_each_entry(group, &tomoyo_group_list[idx], list) { -		if (e.group_name != group->group_name) +	list = ¶m->ns->group_list[idx]; +	list_for_each_entry(group, list, head.list) { +		if (e.group_name != group->group_name || +		    atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS)  			continue; -		atomic_inc(&group->users); +		atomic_inc(&group->head.users);  		found = true;  		break;  	} @@ -120,15 +117,14 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)  		struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));  		if (entry) {  			INIT_LIST_HEAD(&entry->member_list); -			atomic_set(&entry->users, 1); -			list_add_tail_rcu(&entry->list, -					  &tomoyo_group_list[idx]); +			atomic_set(&entry->head.users, 1); +			list_add_tail_rcu(&entry->head.list, list);  			group = entry;  			found = true;  		}  	}  	mutex_unlock(&tomoyo_policy_lock); - out: +out:  	tomoyo_put_name(e.group_name);  	return found ? group : NULL;  } @@ -153,7 +149,6 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)  	struct tomoyo_name *ptr;  	unsigned int hash;  	int len; -	int allocated_len;  	struct list_head *head;  	if (!name) @@ -163,120 +158,44 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)  	head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];  	if (mutex_lock_interruptible(&tomoyo_policy_lock))  		return NULL; -	list_for_each_entry(ptr, head, list) { -		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) +	list_for_each_entry(ptr, head, head.list) { +		if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || +		    atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)  			continue; -		atomic_inc(&ptr->users); +		atomic_inc(&ptr->head.users);  		goto out;  	}  	ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); -	allocated_len = ptr ? ksize(ptr) : 0; -	if (!ptr || (tomoyo_quota_for_policy && -		     atomic_read(&tomoyo_policy_memory_size) + allocated_len -		     > tomoyo_quota_for_policy)) { +	if (tomoyo_memory_ok(ptr)) { +		ptr->entry.name = ((char *) ptr) + sizeof(*ptr); +		memmove((char *) ptr->entry.name, name, len); +		atomic_set(&ptr->head.users, 1); +		tomoyo_fill_path_info(&ptr->entry); +		list_add_tail(&ptr->head.list, head); +	} else {  		kfree(ptr);  		ptr = NULL; -		tomoyo_warn_oom(__func__); -		goto out;  	} -	atomic_add(allocated_len, &tomoyo_policy_memory_size); -	ptr->entry.name = ((char *) ptr) + sizeof(*ptr); -	memmove((char *) ptr->entry.name, name, len); -	atomic_set(&ptr->users, 1); -	tomoyo_fill_path_info(&ptr->entry); -	list_add_tail(&ptr->list, head); - out: +out:  	mutex_unlock(&tomoyo_policy_lock);  	return ptr ? &ptr->entry : NULL;  } +/* Initial namespace.*/ +struct tomoyo_policy_namespace tomoyo_kernel_namespace; +  /**   * tomoyo_mm_init - Initialize mm related code.   */  void __init tomoyo_mm_init(void)  {  	int idx; - -	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) -		INIT_LIST_HEAD(&tomoyo_policy_list[idx]); -	for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) -		INIT_LIST_HEAD(&tomoyo_group_list[idx]);  	for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)  		INIT_LIST_HEAD(&tomoyo_name_list[idx]); +	tomoyo_kernel_namespace.name = "<kernel>"; +	tomoyo_init_policy_namespace(&tomoyo_kernel_namespace); +	tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace;  	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); -	tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); +	tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>");  	list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); -	idx = tomoyo_read_lock(); -	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) -		panic("Can't register tomoyo_kernel_domain"); -	{ -		/* Load built-in policy. */ -		tomoyo_write_transition_control("/sbin/hotplug", false, -					TOMOYO_TRANSITION_CONTROL_INITIALIZE); -		tomoyo_write_transition_control("/sbin/modprobe", false, -					TOMOYO_TRANSITION_CONTROL_INITIALIZE); -	} -	tomoyo_read_unlock(idx); -} - - -/* Memory allocated for query lists. */ -unsigned int tomoyo_query_memory_size; -/* Quota for holding query lists. */ -unsigned int tomoyo_quota_for_query; - -/** - * tomoyo_read_memory_counter - Check for memory usage in bytes. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns memory usage. - */ -void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) -{ -	if (!head->r.eof) { -		const unsigned int policy -			= atomic_read(&tomoyo_policy_memory_size); -		const unsigned int query = tomoyo_query_memory_size; -		char buffer[64]; - -		memset(buffer, 0, sizeof(buffer)); -		if (tomoyo_quota_for_policy) -			snprintf(buffer, sizeof(buffer) - 1, -				 "   (Quota: %10u)", -				 tomoyo_quota_for_policy); -		else -			buffer[0] = '\0'; -		tomoyo_io_printf(head, "Policy:       %10u%s\n", policy, -				 buffer); -		if (tomoyo_quota_for_query) -			snprintf(buffer, sizeof(buffer) - 1, -				 "   (Quota: %10u)", -				 tomoyo_quota_for_query); -		else -			buffer[0] = '\0'; -		tomoyo_io_printf(head, "Query lists:  %10u%s\n", query, -				 buffer); -		tomoyo_io_printf(head, "Total:        %10u\n", policy + query); -		head->r.eof = true; -	} -} - -/** - * tomoyo_write_memory_quota - Set memory quota. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. - */ -int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) -{ -	char *data = head->write_buf; -	unsigned int size; - -	if (sscanf(data, "Policy: %u", &size) == 1) -		tomoyo_quota_for_policy = size; -	else if (sscanf(data, "Query lists: %u", &size) == 1) -		tomoyo_quota_for_query = size; -	return 0;  } diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 82bf8c2390b..390c646013c 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -1,28 +1,22 @@  /*   * security/tomoyo/mount.c   * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/slab.h>  #include "common.h" -/* Keywords for mount restrictions. */ - -/* Allow to call 'mount --bind /source_dir /dest_dir' */ -#define TOMOYO_MOUNT_BIND_KEYWORD                        "--bind" -/* Allow to call 'mount --move /old_dir    /new_dir ' */ -#define TOMOYO_MOUNT_MOVE_KEYWORD                        "--move" -/* Allow to call 'mount -o remount /dir             ' */ -#define TOMOYO_MOUNT_REMOUNT_KEYWORD                     "--remount" -/* Allow to call 'mount --make-unbindable /dir'       */ -#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD             "--make-unbindable" -/* Allow to call 'mount --make-private /dir'          */ -#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD                "--make-private" -/* Allow to call 'mount --make-slave /dir'            */ -#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD                  "--make-slave" -/* Allow to call 'mount --make-shared /dir'           */ -#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD                 "--make-shared" +/* String table for special mount operations. */ +static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = { +	[TOMOYO_MOUNT_BIND]            = "--bind", +	[TOMOYO_MOUNT_MOVE]            = "--move", +	[TOMOYO_MOUNT_REMOUNT]         = "--remount", +	[TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", +	[TOMOYO_MOUNT_MAKE_PRIVATE]    = "--make-private", +	[TOMOYO_MOUNT_MAKE_SLAVE]      = "--make-slave", +	[TOMOYO_MOUNT_MAKE_SHARED]     = "--make-shared", +};  /**   * tomoyo_audit_mount_log - Audit mount log. @@ -33,50 +27,42 @@   */  static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)  { -	const char *dev = r->param.mount.dev->name; -	const char *dir = r->param.mount.dir->name; -	const char *type = r->param.mount.type->name; -	const unsigned long flags = r->param.mount.flags; -	if (r->granted) -		return 0; -	if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) -		tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags); -	else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) -		 || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) -		tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir, -				flags); -	else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || -		 !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || -		 !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || -		 !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) -		tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags); -	else -		tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir, -				flags); -	return tomoyo_supervisor(r, -				 TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", -				 tomoyo_pattern(r->param.mount.dev), -				 tomoyo_pattern(r->param.mount.dir), type, -				 flags); +	return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n", +				 r->param.mount.dev->name, +				 r->param.mount.dir->name, +				 r->param.mount.type->name, +				 r->param.mount.flags);  } +/** + * tomoyo_check_mount_acl - Check permission for path path path number operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */  static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,  				   const struct tomoyo_acl_info *ptr)  {  	const struct tomoyo_mount_acl *acl =  		container_of(ptr, typeof(*acl), head); -	return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) && -		tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) && -		tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) && +	return tomoyo_compare_number_union(r->param.mount.flags, +					   &acl->flags) && +		tomoyo_compare_name_union(r->param.mount.type, +					  &acl->fs_type) && +		tomoyo_compare_name_union(r->param.mount.dir, +					  &acl->dir_name) &&  		(!r->param.mount.need_dev || -		 tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name)); +		 tomoyo_compare_name_union(r->param.mount.dev, +					   &acl->dev_name));  }  /**   * tomoyo_mount_acl - Check permission for mount() operation.   *   * @r:        Pointer to "struct tomoyo_request_info". - * @dev_name: Name of device file. + * @dev_name: Name of device file. Maybe NULL.   * @dir:      Pointer to "struct path".   * @type:     Name of filesystem type.   * @flags:    Mount options. @@ -85,9 +71,12 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, -			    struct path *dir, char *type, unsigned long flags) +static int tomoyo_mount_acl(struct tomoyo_request_info *r, +			    const char *dev_name, +			    struct path *dir, const char *type, +			    unsigned long flags)  { +	struct tomoyo_obj_info obj = { };  	struct path path;  	struct file_system_type *fstype = NULL;  	const char *requested_type = NULL; @@ -98,6 +87,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,  	struct tomoyo_path_info rdir;  	int need_dev = 0;  	int error = -ENOMEM; +	r->obj = &obj;  	/* Get fstype. */  	requested_type = tomoyo_encode(type); @@ -107,6 +97,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,  	tomoyo_fill_path_info(&rtype);  	/* Get mount point. */ +	obj.path2 = *dir;  	requested_dir_name = tomoyo_realpath_from_path(dir);  	if (!requested_dir_name) {  		error = -ENOMEM; @@ -116,15 +107,15 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,  	tomoyo_fill_path_info(&rdir);  	/* Compare fs name. */ -	if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { +	if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) {  		/* dev_name is ignored. */ -	} else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || -		   !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || -		   !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || -		   !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { +	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] || +		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] || +		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] || +		   type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) {  		/* dev_name is ignored. */ -	} else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || -		   !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { +	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] || +		   type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {  		need_dev = -1; /* dev_name is a directory */  	} else {  		fstype = get_fs_type(type); @@ -138,10 +129,11 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,  	}  	if (need_dev) {  		/* Get mount point or device file. */ -		if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { +		if (!dev_name || kern_path(dev_name, LOOKUP_FOLLOW, &path)) {  			error = -ENOENT;  			goto out;  		} +		obj.path1 = path;  		requested_dev_name = tomoyo_realpath_from_path(&path);  		if (!requested_dev_name) {  			error = -ENOENT; @@ -175,22 +167,26 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,  	if (fstype)  		put_filesystem(fstype);  	kfree(requested_type); +	/* Drop refcount obtained by kern_path(). */ +	if (obj.path1.dentry) +		path_put(&obj.path1);  	return error;  }  /**   * tomoyo_mount_permission - Check permission for mount() operation.   * - * @dev_name:  Name of device file. + * @dev_name:  Name of device file. Maybe NULL.   * @path:      Pointer to "struct path". - * @type:      Name of filesystem type. May be NULL. + * @type:      Name of filesystem type. Maybe NULL.   * @flags:     Mount options. - * @data_page: Optional data. May be NULL. + * @data_page: Optional data. Maybe NULL.   *   * Returns 0 on success, negative value otherwise.   */ -int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, -			    unsigned long flags, void *data_page) +int tomoyo_mount_permission(const char *dev_name, struct path *path, +			    const char *type, unsigned long flags, +			    void *data_page)  {  	struct tomoyo_request_info r;  	int error; @@ -202,32 +198,34 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,  	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)  		flags &= ~MS_MGC_MSK;  	if (flags & MS_REMOUNT) { -		type = TOMOYO_MOUNT_REMOUNT_KEYWORD; +		type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT];  		flags &= ~MS_REMOUNT; -	} -	if (flags & MS_MOVE) { -		type = TOMOYO_MOUNT_MOVE_KEYWORD; -		flags &= ~MS_MOVE; -	} -	if (flags & MS_BIND) { -		type = TOMOYO_MOUNT_BIND_KEYWORD; +	} else if (flags & MS_BIND) { +		type = tomoyo_mounts[TOMOYO_MOUNT_BIND];  		flags &= ~MS_BIND; -	} -	if (flags & MS_UNBINDABLE) { -		type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD; -		flags &= ~MS_UNBINDABLE; -	} -	if (flags & MS_PRIVATE) { -		type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD; +	} else if (flags & MS_SHARED) { +		if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) +			return -EINVAL; +		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]; +		flags &= ~MS_SHARED; +	} else if (flags & MS_PRIVATE) { +		if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) +			return -EINVAL; +		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE];  		flags &= ~MS_PRIVATE; -	} -	if (flags & MS_SLAVE) { -		type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD; +	} else if (flags & MS_SLAVE) { +		if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) +			return -EINVAL; +		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE];  		flags &= ~MS_SLAVE; -	} -	if (flags & MS_SHARED) { -		type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD; -		flags &= ~MS_SHARED; +	} else if (flags & MS_UNBINDABLE) { +		if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) +			return -EINVAL; +		type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE]; +		flags &= ~MS_UNBINDABLE; +	} else if (flags & MS_MOVE) { +		type = tomoyo_mounts[TOMOYO_MOUNT_MOVE]; +		flags &= ~MS_MOVE;  	}  	if (!type)  		type = "<NULL>"; @@ -236,49 +234,3 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,  	tomoyo_read_unlock(idx);  	return error;  } - -static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, -				  const struct tomoyo_acl_info *b) -{ -	const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); -	const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); -	return tomoyo_same_acl_head(&p1->head, &p2->head) && -		tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && -		tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && -		tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && -		tomoyo_same_number_union(&p1->flags, &p2->flags); -} - -/** - * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list. - * - * @data:      String to parse. - * @domain:    Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, -		       const bool is_delete) -{ -	struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; -	int error = is_delete ? -ENOENT : -ENOMEM; -	char *w[4]; -	if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) -		return -EINVAL; -	if (!tomoyo_parse_name_union(w[0], &e.dev_name) || -	    !tomoyo_parse_name_union(w[1], &e.dir_name) || -	    !tomoyo_parse_name_union(w[2], &e.fs_type) || -	    !tomoyo_parse_number_union(w[3], &e.flags)) -		goto out; -	error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, -				     tomoyo_same_mount_acl, NULL); - out: -	tomoyo_put_name_union(&e.dev_name); -	tomoyo_put_name_union(&e.dir_name); -	tomoyo_put_name_union(&e.fs_type); -	tomoyo_put_number_union(&e.flags); -	return error; -} diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c new file mode 100644 index 00000000000..97527710a72 --- /dev/null +++ b/security/tomoyo/network.c @@ -0,0 +1,771 @@ +/* + * security/tomoyo/network.c + * + * Copyright (C) 2005-2011  NTT DATA CORPORATION + */ + +#include "common.h" +#include <linux/slab.h> + +/* Structure for holding inet domain socket's address. */ +struct tomoyo_inet_addr_info { +	__be16 port;           /* In network byte order. */ +	const __be32 *address; /* In network byte order. */ +	bool is_ipv6; +}; + +/* Structure for holding unix domain socket's address. */ +struct tomoyo_unix_addr_info { +	u8 *addr; /* This may not be '\0' terminated string. */ +	unsigned int addr_len; +}; + +/* Structure for holding socket address. */ +struct tomoyo_addr_info { +	u8 protocol; +	u8 operation; +	struct tomoyo_inet_addr_info inet; +	struct tomoyo_unix_addr_info unix0; +}; + +/* String table for socket's protocols. */ +const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = { +	[SOCK_STREAM]    = "stream", +	[SOCK_DGRAM]     = "dgram", +	[SOCK_RAW]       = "raw", +	[SOCK_SEQPACKET] = "seqpacket", +	[0] = " ", /* Dummy for avoiding NULL pointer dereference. */ +	[4] = " ", /* Dummy for avoiding NULL pointer dereference. */ +}; + +/** + * tomoyo_parse_ipaddr_union - Parse an IP address. + * + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr:   Pointer to "struct tomoyo_ipaddr_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param, +			       struct tomoyo_ipaddr_union *ptr) +{ +	u8 * const min = ptr->ip[0].in6_u.u6_addr8; +	u8 * const max = ptr->ip[1].in6_u.u6_addr8; +	char *address = tomoyo_read_token(param); +	const char *end; + +	if (!strchr(address, ':') && +	    in4_pton(address, -1, min, '-', &end) > 0) { +		ptr->is_ipv6 = false; +		if (!*end) +			ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0]; +		else if (*end++ != '-' || +			 in4_pton(end, -1, max, '\0', &end) <= 0 || *end) +			return false; +		return true; +	} +	if (in6_pton(address, -1, min, '-', &end) > 0) { +		ptr->is_ipv6 = true; +		if (!*end) +			memmove(max, min, sizeof(u16) * 8); +		else if (*end++ != '-' || +			 in6_pton(end, -1, max, '\0', &end) <= 0 || *end) +			return false; +		return true; +	} +	return false; +} + +/** + * tomoyo_print_ipv4 - Print an IPv4 address. + * + * @buffer:     Buffer to write to. + * @buffer_len: Size of @buffer. + * @min_ip:     Pointer to __be32. + * @max_ip:     Pointer to __be32. + * + * Returns nothing. + */ +static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len, +			      const __be32 *min_ip, const __be32 *max_ip) +{ +	snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip, +		 *min_ip == *max_ip ? '\0' : '-', max_ip); +} + +/** + * tomoyo_print_ipv6 - Print an IPv6 address. + * + * @buffer:     Buffer to write to. + * @buffer_len: Size of @buffer. + * @min_ip:     Pointer to "struct in6_addr". + * @max_ip:     Pointer to "struct in6_addr". + * + * Returns nothing. + */ +static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len, +			      const struct in6_addr *min_ip, +			      const struct in6_addr *max_ip) +{ +	snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip, +		 !memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip); +} + +/** + * tomoyo_print_ip - Print an IP address. + * + * @buf:  Buffer to write to. + * @size: Size of @buf. + * @ptr:  Pointer to "struct ipaddr_union". + * + * Returns nothing. + */ +void tomoyo_print_ip(char *buf, const unsigned int size, +		     const struct tomoyo_ipaddr_union *ptr) +{ +	if (ptr->is_ipv6) +		tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]); +	else +		tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0], +				  &ptr->ip[1].s6_addr32[0]); +} + +/* + * Mapping table from "enum tomoyo_network_acl_index" to + * "enum tomoyo_mac_index" for inet domain socket. + */ +static const u8 tomoyo_inet2mac +[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = { +	[SOCK_STREAM] = { +		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_STREAM_BIND, +		[TOMOYO_NETWORK_LISTEN]  = +		TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN, +		[TOMOYO_NETWORK_CONNECT] = +		TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT, +	}, +	[SOCK_DGRAM] = { +		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND, +		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND, +	}, +	[SOCK_RAW]    = { +		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_INET_RAW_BIND, +		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_INET_RAW_SEND, +	}, +}; + +/* + * Mapping table from "enum tomoyo_network_acl_index" to + * "enum tomoyo_mac_index" for unix domain socket. + */ +static const u8 tomoyo_unix2mac +[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = { +	[SOCK_STREAM] = { +		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND, +		[TOMOYO_NETWORK_LISTEN]  = +		TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN, +		[TOMOYO_NETWORK_CONNECT] = +		TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT, +	}, +	[SOCK_DGRAM] = { +		[TOMOYO_NETWORK_BIND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND, +		[TOMOYO_NETWORK_SEND]    = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND, +	}, +	[SOCK_SEQPACKET] = { +		[TOMOYO_NETWORK_BIND]    = +		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND, +		[TOMOYO_NETWORK_LISTEN]  = +		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, +		[TOMOYO_NETWORK_CONNECT] = +		TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, +	}, +}; + +/** + * tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ +static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a, +				 const struct tomoyo_acl_info *b) +{ +	const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head); +	const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head); + +	return p1->protocol == p2->protocol && +		tomoyo_same_ipaddr_union(&p1->address, &p2->address) && +		tomoyo_same_number_union(&p1->port, &p2->port); +} + +/** + * tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ +static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a, +				 const struct tomoyo_acl_info *b) +{ +	const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head); +	const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head); + +	return p1->protocol == p2->protocol && +		tomoyo_same_name_union(&p1->name, &p2->name); +} + +/** + * tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry. + * + * @a:         Pointer to "struct tomoyo_acl_info". + * @b:         Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ +static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a, +				  struct tomoyo_acl_info *b, +				  const bool is_delete) +{ +	u8 * const a_perm = +		&container_of(a, struct tomoyo_inet_acl, head)->perm; +	u8 perm = *a_perm; +	const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm; + +	if (is_delete) +		perm &= ~b_perm; +	else +		perm |= b_perm; +	*a_perm = perm; +	return !perm; +} + +/** + * tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry. + * + * @a:         Pointer to "struct tomoyo_acl_info". + * @b:         Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ +static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a, +				  struct tomoyo_acl_info *b, +				  const bool is_delete) +{ +	u8 * const a_perm = +		&container_of(a, struct tomoyo_unix_acl, head)->perm; +	u8 perm = *a_perm; +	const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm; + +	if (is_delete) +		perm &= ~b_perm; +	else +		perm |= b_perm; +	*a_perm = perm; +	return !perm; +} + +/** + * tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_write_inet_network(struct tomoyo_acl_param *param) +{ +	struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL }; +	int error = -EINVAL; +	u8 type; +	const char *protocol = tomoyo_read_token(param); +	const char *operation = tomoyo_read_token(param); + +	for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++) +		if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol])) +			break; +	for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++) +		if (tomoyo_permstr(operation, tomoyo_socket_keyword[type])) +			e.perm |= 1 << type; +	if (e.protocol == TOMOYO_SOCK_MAX || !e.perm) +		return -EINVAL; +	if (param->data[0] == '@') { +		param->data++; +		e.address.group = +			tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP); +		if (!e.address.group) +			return -ENOMEM; +	} else { +		if (!tomoyo_parse_ipaddr_union(param, &e.address)) +			goto out; +	} +	if (!tomoyo_parse_number_union(param, &e.port) || +	    e.port.values[1] > 65535) +		goto out; +	error = tomoyo_update_domain(&e.head, sizeof(e), param, +				     tomoyo_same_inet_acl, +				     tomoyo_merge_inet_acl); +out: +	tomoyo_put_group(e.address.group); +	tomoyo_put_number_union(&e.port); +	return error; +} + +/** + * tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_unix_network(struct tomoyo_acl_param *param) +{ +	struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL }; +	int error; +	u8 type; +	const char *protocol = tomoyo_read_token(param); +	const char *operation = tomoyo_read_token(param); + +	for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++) +		if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol])) +			break; +	for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++) +		if (tomoyo_permstr(operation, tomoyo_socket_keyword[type])) +			e.perm |= 1 << type; +	if (e.protocol == TOMOYO_SOCK_MAX || !e.perm) +		return -EINVAL; +	if (!tomoyo_parse_name_union(param, &e.name)) +		return -EINVAL; +	error = tomoyo_update_domain(&e.head, sizeof(e), param, +				     tomoyo_same_unix_acl, +				     tomoyo_merge_unix_acl); +	tomoyo_put_name_union(&e.name); +	return error; +} + +/** + * tomoyo_audit_net_log - Audit network log. + * + * @r:         Pointer to "struct tomoyo_request_info". + * @family:    Name of socket family ("inet" or "unix"). + * @protocol:  Name of protocol in @family. + * @operation: Name of socket operation. + * @address:   Name of address. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_net_log(struct tomoyo_request_info *r, +				const char *family, const u8 protocol, +				const u8 operation, const char *address) +{ +	return tomoyo_supervisor(r, "network %s %s %s %s\n", family, +				 tomoyo_proto_keyword[protocol], +				 tomoyo_socket_keyword[operation], address); +} + +/** + * tomoyo_audit_inet_log - Audit INET network log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_inet_log(struct tomoyo_request_info *r) +{ +	char buf[128]; +	int len; +	const __be32 *address = r->param.inet_network.address; + +	if (r->param.inet_network.is_ipv6) +		tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *) +				  address, (const struct in6_addr *) address); +	else +		tomoyo_print_ipv4(buf, sizeof(buf), address, address); +	len = strlen(buf); +	snprintf(buf + len, sizeof(buf) - len, " %u", +		 r->param.inet_network.port); +	return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol, +				    r->param.inet_network.operation, buf); +} + +/** + * tomoyo_audit_unix_log - Audit UNIX network log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_unix_log(struct tomoyo_request_info *r) +{ +	return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol, +				    r->param.unix_network.operation, +				    r->param.unix_network.address->name); +} + +/** + * tomoyo_check_inet_acl - Check permission for inet domain socket operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r, +				  const struct tomoyo_acl_info *ptr) +{ +	const struct tomoyo_inet_acl *acl = +		container_of(ptr, typeof(*acl), head); +	const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4; + +	if (!(acl->perm & (1 << r->param.inet_network.operation)) || +	    !tomoyo_compare_number_union(r->param.inet_network.port, +					 &acl->port)) +		return false; +	if (acl->address.group) +		return tomoyo_address_matches_group +			(r->param.inet_network.is_ipv6, +			 r->param.inet_network.address, acl->address.group); +	return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 && +		memcmp(&acl->address.ip[0], +		       r->param.inet_network.address, size) <= 0 && +		memcmp(r->param.inet_network.address, +		       &acl->address.ip[1], size) <= 0; +} + +/** + * tomoyo_check_unix_acl - Check permission for unix domain socket operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r, +				  const struct tomoyo_acl_info *ptr) +{ +	const struct tomoyo_unix_acl *acl = +		container_of(ptr, typeof(*acl), head); + +	return (acl->perm & (1 << r->param.unix_network.operation)) && +		tomoyo_compare_name_union(r->param.unix_network.address, +					  &acl->name); +} + +/** + * tomoyo_inet_entry - Check permission for INET network operation. + * + * @address: Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_inet_entry(const struct tomoyo_addr_info *address) +{ +	const int idx = tomoyo_read_lock(); +	struct tomoyo_request_info r; +	int error = 0; +	const u8 type = tomoyo_inet2mac[address->protocol][address->operation]; + +	if (type && tomoyo_init_request_info(&r, NULL, type) +	    != TOMOYO_CONFIG_DISABLED) { +		r.param_type = TOMOYO_TYPE_INET_ACL; +		r.param.inet_network.protocol = address->protocol; +		r.param.inet_network.operation = address->operation; +		r.param.inet_network.is_ipv6 = address->inet.is_ipv6; +		r.param.inet_network.address = address->inet.address; +		r.param.inet_network.port = ntohs(address->inet.port); +		do { +			tomoyo_check_acl(&r, tomoyo_check_inet_acl); +			error = tomoyo_audit_inet_log(&r); +		} while (error == TOMOYO_RETRY_REQUEST); +	} +	tomoyo_read_unlock(idx); +	return error; +} + +/** + * tomoyo_check_inet_address - Check permission for inet domain socket's operation. + * + * @addr:     Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * @port:     Port number. + * @address:  Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_check_inet_address(const struct sockaddr *addr, +				     const unsigned int addr_len, +				     const u16 port, +				     struct tomoyo_addr_info *address) +{ +	struct tomoyo_inet_addr_info *i = &address->inet; + +	switch (addr->sa_family) { +	case AF_INET6: +		if (addr_len < SIN6_LEN_RFC2133) +			goto skip; +		i->is_ipv6 = true; +		i->address = (__be32 *) +			((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; +		i->port = ((struct sockaddr_in6 *) addr)->sin6_port; +		break; +	case AF_INET: +		if (addr_len < sizeof(struct sockaddr_in)) +			goto skip; +		i->is_ipv6 = false; +		i->address = (__be32 *) +			&((struct sockaddr_in *) addr)->sin_addr; +		i->port = ((struct sockaddr_in *) addr)->sin_port; +		break; +	default: +		goto skip; +	} +	if (address->protocol == SOCK_RAW) +		i->port = htons(port); +	return tomoyo_inet_entry(address); +skip: +	return 0; +} + +/** + * tomoyo_unix_entry - Check permission for UNIX network operation. + * + * @address: Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_unix_entry(const struct tomoyo_addr_info *address) +{ +	const int idx = tomoyo_read_lock(); +	struct tomoyo_request_info r; +	int error = 0; +	const u8 type = tomoyo_unix2mac[address->protocol][address->operation]; + +	if (type && tomoyo_init_request_info(&r, NULL, type) +	    != TOMOYO_CONFIG_DISABLED) { +		char *buf = address->unix0.addr; +		int len = address->unix0.addr_len - sizeof(sa_family_t); + +		if (len <= 0) { +			buf = "anonymous"; +			len = 9; +		} else if (buf[0]) { +			len = strnlen(buf, len); +		} +		buf = tomoyo_encode2(buf, len); +		if (buf) { +			struct tomoyo_path_info addr; + +			addr.name = buf; +			tomoyo_fill_path_info(&addr); +			r.param_type = TOMOYO_TYPE_UNIX_ACL; +			r.param.unix_network.protocol = address->protocol; +			r.param.unix_network.operation = address->operation; +			r.param.unix_network.address = &addr; +			do { +				tomoyo_check_acl(&r, tomoyo_check_unix_acl); +				error = tomoyo_audit_unix_log(&r); +			} while (error == TOMOYO_RETRY_REQUEST); +			kfree(buf); +		} else +			error = -ENOMEM; +	} +	tomoyo_read_unlock(idx); +	return error; +} + +/** + * tomoyo_check_unix_address - Check permission for unix domain socket's operation. + * + * @addr:     Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * @address:  Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_check_unix_address(struct sockaddr *addr, +				     const unsigned int addr_len, +				     struct tomoyo_addr_info *address) +{ +	struct tomoyo_unix_addr_info *u = &address->unix0; + +	if (addr->sa_family != AF_UNIX) +		return 0; +	u->addr = ((struct sockaddr_un *) addr)->sun_path; +	u->addr_len = addr_len; +	return tomoyo_unix_entry(address); +} + +/** + * tomoyo_kernel_service - Check whether I'm kernel service or not. + * + * Returns true if I'm kernel service, false otherwise. + */ +static bool tomoyo_kernel_service(void) +{ +	/* Nothing to do if I am a kernel service. */ +	return segment_eq(get_fs(), KERNEL_DS); +} + +/** + * tomoyo_sock_family - Get socket's family. + * + * @sk: Pointer to "struct sock". + * + * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. + */ +static u8 tomoyo_sock_family(struct sock *sk) +{ +	u8 family; + +	if (tomoyo_kernel_service()) +		return 0; +	family = sk->sk_family; +	switch (family) { +	case PF_INET: +	case PF_INET6: +	case PF_UNIX: +		return family; +	default: +		return 0; +	} +} + +/** + * tomoyo_socket_listen_permission - Check permission for listening a socket. + * + * @sock: Pointer to "struct socket". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_listen_permission(struct socket *sock) +{ +	struct tomoyo_addr_info address; +	const u8 family = tomoyo_sock_family(sock->sk); +	const unsigned int type = sock->type; +	struct sockaddr_storage addr; +	int addr_len; + +	if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) +		return 0; +	{ +		const int error = sock->ops->getname(sock, (struct sockaddr *) +						     &addr, &addr_len, 0); + +		if (error) +			return error; +	} +	address.protocol = type; +	address.operation = TOMOYO_NETWORK_LISTEN; +	if (family == PF_UNIX) +		return tomoyo_check_unix_address((struct sockaddr *) &addr, +						 addr_len, &address); +	return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len, +					 0, &address); +} + +/** + * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket. + * + * @sock:     Pointer to "struct socket". + * @addr:     Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_connect_permission(struct socket *sock, +				     struct sockaddr *addr, int addr_len) +{ +	struct tomoyo_addr_info address; +	const u8 family = tomoyo_sock_family(sock->sk); +	const unsigned int type = sock->type; + +	if (!family) +		return 0; +	address.protocol = type; +	switch (type) { +	case SOCK_DGRAM: +	case SOCK_RAW: +		address.operation = TOMOYO_NETWORK_SEND; +		break; +	case SOCK_STREAM: +	case SOCK_SEQPACKET: +		address.operation = TOMOYO_NETWORK_CONNECT; +		break; +	default: +		return 0; +	} +	if (family == PF_UNIX) +		return tomoyo_check_unix_address(addr, addr_len, &address); +	return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol, +					 &address); +} + +/** + * tomoyo_socket_bind_permission - Check permission for setting the local address of a socket. + * + * @sock:     Pointer to "struct socket". + * @addr:     Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, +				  int addr_len) +{ +	struct tomoyo_addr_info address; +	const u8 family = tomoyo_sock_family(sock->sk); +	const unsigned int type = sock->type; + +	if (!family) +		return 0; +	switch (type) { +	case SOCK_STREAM: +	case SOCK_DGRAM: +	case SOCK_RAW: +	case SOCK_SEQPACKET: +		address.protocol = type; +		address.operation = TOMOYO_NETWORK_BIND; +		break; +	default: +		return 0; +	} +	if (family == PF_UNIX) +		return tomoyo_check_unix_address(addr, addr_len, &address); +	return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol, +					 &address); +} + +/** + * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram. + * + * @sock: Pointer to "struct socket". + * @msg:  Pointer to "struct msghdr". + * @size: Unused. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg, +				     int size) +{ +	struct tomoyo_addr_info address; +	const u8 family = tomoyo_sock_family(sock->sk); +	const unsigned int type = sock->type; + +	if (!msg->msg_name || !family || +	    (type != SOCK_DGRAM && type != SOCK_RAW)) +		return 0; +	address.protocol = type; +	address.operation = TOMOYO_NETWORK_SEND; +	if (family == PF_UNIX) +		return tomoyo_check_unix_address((struct sockaddr *) +						 msg->msg_name, +						 msg->msg_namelen, &address); +	return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name, +					 msg->msg_namelen, +					 sock->sk->sk_protocol, &address); +} diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 1d0bf8fa192..a3386d11942 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -1,32 +1,26 @@  /*   * security/tomoyo/realpath.c   * - * Pathname calculation functions for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */ -#include <linux/types.h> -#include <linux/mount.h> -#include <linux/mnt_namespace.h> -#include <linux/fs_struct.h> -#include <linux/magic.h> -#include <linux/slab.h> -#include <net/sock.h>  #include "common.h" +#include <linux/magic.h>  /** - * tomoyo_encode: Convert binary string to ascii string. + * tomoyo_encode2 - Encode binary string to ascii string.   * - * @str: String in binary format. + * @str:     String in binary format. + * @str_len: Size of @str in byte.   *   * Returns pointer to @str in ascii format on success, NULL otherwise.   *   * This function uses kzalloc(), so caller must kfree() if this function   * didn't return NULL.   */ -char *tomoyo_encode(const char *str) +char *tomoyo_encode2(const char *str, int str_len)  { +	int i;  	int len = 0;  	const char *p = str;  	char *cp; @@ -34,8 +28,9 @@ char *tomoyo_encode(const char *str)  	if (!p)  		return NULL; -	while (*p) { -		const unsigned char c = *p++; +	for (i = 0; i < str_len; i++) { +		const unsigned char c = p[i]; +  		if (c == '\\')  			len += 2;  		else if (c > ' ' && c < 127) @@ -50,8 +45,8 @@ char *tomoyo_encode(const char *str)  		return NULL;  	cp0 = cp;  	p = str; -	while (*p) { -		const unsigned char c = *p++; +	for (i = 0; i < str_len; i++) { +		const unsigned char c = p[i];  		if (c == '\\') {  			*cp++ = '\\'; @@ -69,6 +64,175 @@ char *tomoyo_encode(const char *str)  }  /** + * tomoyo_encode - Encode binary string to ascii string. + * + * @str: String in binary format. + * + * Returns pointer to @str in ascii format on success, NULL otherwise. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +char *tomoyo_encode(const char *str) +{ +	return str ? tomoyo_encode2(str, strlen(str)) : NULL; +} + +/** + * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. + * + * @path:   Pointer to "struct path". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer on success, an error code otherwise. + * + * If dentry is a directory, trailing '/' is appended. + */ +static char *tomoyo_get_absolute_path(struct path *path, char * const buffer, +				      const int buflen) +{ +	char *pos = ERR_PTR(-ENOMEM); +	if (buflen >= 256) { +		/* go to whatever namespace root we are under */ +		pos = d_absolute_path(path, buffer, buflen - 1); +		if (!IS_ERR(pos) && *pos == '/' && pos[1]) { +			struct inode *inode = path->dentry->d_inode; +			if (inode && S_ISDIR(inode->i_mode)) { +				buffer[buflen - 2] = '/'; +				buffer[buflen - 1] = '\0'; +			} +		} +	} +	return pos; +} + +/** + * tomoyo_get_dentry_path - Get the path of a dentry. + * + * @dentry: Pointer to "struct dentry". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer on success, an error code otherwise. + * + * If dentry is a directory, trailing '/' is appended. + */ +static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer, +				    const int buflen) +{ +	char *pos = ERR_PTR(-ENOMEM); +	if (buflen >= 256) { +		pos = dentry_path_raw(dentry, buffer, buflen - 1); +		if (!IS_ERR(pos) && *pos == '/' && pos[1]) { +			struct inode *inode = dentry->d_inode; +			if (inode && S_ISDIR(inode->i_mode)) { +				buffer[buflen - 2] = '/'; +				buffer[buflen - 1] = '\0'; +			} +		} +	} +	return pos; +} + +/** + * tomoyo_get_local_path - Get the path of a dentry. + * + * @dentry: Pointer to "struct dentry". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer on success, an error code otherwise. + */ +static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, +				   const int buflen) +{ +	struct super_block *sb = dentry->d_sb; +	char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen); +	if (IS_ERR(pos)) +		return pos; +	/* Convert from $PID to self if $PID is current thread. */ +	if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { +		char *ep; +		const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); +		if (*ep == '/' && pid && pid == +		    task_tgid_nr_ns(current, sb->s_fs_info)) { +			pos = ep - 5; +			if (pos < buffer) +				goto out; +			memmove(pos, "/self", 5); +		} +		goto prepend_filesystem_name; +	} +	/* Use filesystem name for unnamed devices. */ +	if (!MAJOR(sb->s_dev)) +		goto prepend_filesystem_name; +	{ +		struct inode *inode = sb->s_root->d_inode; +		/* +		 * Use filesystem name if filesystem does not support rename() +		 * operation. +		 */ +		if (!inode->i_op->rename) +			goto prepend_filesystem_name; +	} +	/* Prepend device name. */ +	{ +		char name[64]; +		int name_len; +		const dev_t dev = sb->s_dev; +		name[sizeof(name) - 1] = '\0'; +		snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), +			 MINOR(dev)); +		name_len = strlen(name); +		pos -= name_len; +		if (pos < buffer) +			goto out; +		memmove(pos, name, name_len); +		return pos; +	} +	/* Prepend filesystem name. */ +prepend_filesystem_name: +	{ +		const char *name = sb->s_type->name; +		const int name_len = strlen(name); +		pos -= name_len + 1; +		if (pos < buffer) +			goto out; +		memmove(pos, name, name_len); +		pos[name_len] = ':'; +	} +	return pos; +out: +	return ERR_PTR(-ENOMEM); +} + +/** + * tomoyo_get_socket_name - Get the name of a socket. + * + * @path:   Pointer to "struct path". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer. + */ +static char *tomoyo_get_socket_name(struct path *path, char * const buffer, +				    const int buflen) +{ +	struct inode *inode = path->dentry->d_inode; +	struct socket *sock = inode ? SOCKET_I(inode) : NULL; +	struct sock *sk = sock ? sock->sk : NULL; +	if (sk) { +		snprintf(buffer, buflen, "socket:[family=%u:type=%u:" +			 "protocol=%u]", sk->sk_family, sk->sk_type, +			 sk->sk_protocol); +	} else { +		snprintf(buffer, buflen, "socket:[unknown]"); +	} +	return buffer; +} + +/**   * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.   *   * @path: Pointer to "struct path". @@ -89,55 +253,50 @@ char *tomoyo_realpath_from_path(struct path *path)  	char *name = NULL;  	unsigned int buf_len = PAGE_SIZE / 2;  	struct dentry *dentry = path->dentry; -	bool is_dir; +	struct super_block *sb;  	if (!dentry)  		return NULL; -	is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); +	sb = dentry->d_sb;  	while (1) { -		struct path ns_root = { .mnt = NULL, .dentry = NULL };  		char *pos; +		struct inode *inode;  		buf_len <<= 1;  		kfree(buf);  		buf = kmalloc(buf_len, GFP_NOFS);  		if (!buf)  			break; +		/* To make sure that pos is '\0' terminated. */ +		buf[buf_len - 1] = '\0';  		/* Get better name for socket. */ -		if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { -			struct inode *inode = dentry->d_inode; -			struct socket *sock = inode ? SOCKET_I(inode) : NULL; -			struct sock *sk = sock ? sock->sk : NULL; -			if (sk) { -				snprintf(buf, buf_len - 1, "socket:[family=%u:" -					 "type=%u:protocol=%u]", sk->sk_family, -					 sk->sk_type, sk->sk_protocol); -			} else { -				snprintf(buf, buf_len - 1, "socket:[unknown]"); -			} -			name = tomoyo_encode(buf); -			break; +		if (sb->s_magic == SOCKFS_MAGIC) { +			pos = tomoyo_get_socket_name(path, buf, buf_len - 1); +			goto encode;  		} -		/* For "socket:[\$]" and "pipe:[\$]". */ +		/* For "pipe:[\$]". */  		if (dentry->d_op && dentry->d_op->d_dname) {  			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); -			if (IS_ERR(pos)) -				continue; -			name = tomoyo_encode(pos); -			break; +			goto encode;  		} -		/* If we don't have a vfsmount, we can't calculate. */ -		if (!path->mnt) -			break; -		/* go to whatever namespace root we are under */ -		pos = __d_path(path, &ns_root, buf, buf_len); -		/* Prepend "/proc" prefix if using internal proc vfs mount. */ -		if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) && -		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { -			pos -= 5; -			if (pos >= buf) -				memcpy(pos, "/proc", 5); -			else -				pos = ERR_PTR(-ENOMEM); +		inode = sb->s_root->d_inode; +		/* +		 * Get local name for filesystems without rename() operation +		 * or dentry without vfsmount. +		 */ +		if (!path->mnt || !inode->i_op->rename) +			pos = tomoyo_get_local_path(path->dentry, buf, +						    buf_len - 1); +		/* Get absolute name for the rest. */ +		else { +			pos = tomoyo_get_absolute_path(path, buf, buf_len - 1); +			/* +			 * Fall back to local name if absolute name is not +			 * available. +			 */ +			if (pos == ERR_PTR(-EINVAL)) +				pos = tomoyo_get_local_path(path->dentry, buf, +							    buf_len - 1);  		} +encode:  		if (IS_ERR(pos))  			continue;  		name = tomoyo_encode(pos); @@ -146,16 +305,6 @@ char *tomoyo_realpath_from_path(struct path *path)  	kfree(buf);  	if (!name)  		tomoyo_warn_oom(__func__); -	else if (is_dir && *name) { -		/* Append trailing '/' if dentry is a directory. */ -		char *pos = name + strlen(name) - 1; -		if (*pos != '/') -			/* -			 * This is OK because tomoyo_encode() reserves space -			 * for appending "/". -			 */ -			*++pos = '/'; -	}  	return name;  } diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index e43d5554b50..179a955b319 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -1,15 +1,131 @@  /* - * security/tomoyo/common.c + * security/tomoyo/securityfs_if.c   * - * Securityfs interface for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/security.h>  #include "common.h"  /** + * tomoyo_check_task_acl - Check permission for task operation. + * + * @r:   Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_task_acl(struct tomoyo_request_info *r, +				  const struct tomoyo_acl_info *ptr) +{ +	const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl), +							 head); +	return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname); +} + +/** + * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface. + * + * @file:  Pointer to "struct file". + * @buf:   Domainname to transit to. + * @count: Size of @buf. + * @ppos:  Unused. + * + * Returns @count on success, negative value otherwise. + * + * If domain transition was permitted but the domain transition failed, this + * function returns error rather than terminating current thread with SIGKILL. + */ +static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, +			      size_t count, loff_t *ppos) +{ +	char *data; +	int error; +	if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10) +		return -ENOMEM; +	data = kzalloc(count + 1, GFP_NOFS); +	if (!data) +		return -ENOMEM; +	if (copy_from_user(data, buf, count)) { +		error = -EFAULT; +		goto out; +	} +	tomoyo_normalize_line(data); +	if (tomoyo_correct_domain(data)) { +		const int idx = tomoyo_read_lock(); +		struct tomoyo_path_info name; +		struct tomoyo_request_info r; +		name.name = data; +		tomoyo_fill_path_info(&name); +		/* Check "task manual_domain_transition" permission. */ +		tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); +		r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL; +		r.param.task.domainname = &name; +		tomoyo_check_acl(&r, tomoyo_check_task_acl); +		if (!r.granted) +			error = -EPERM; +		else { +			struct tomoyo_domain_info *new_domain = +				tomoyo_assign_domain(data, true); +			if (!new_domain) { +				error = -ENOENT; +			} else { +				struct cred *cred = prepare_creds(); +				if (!cred) { +					error = -ENOMEM; +				} else { +					struct tomoyo_domain_info *old_domain = +						cred->security; +					cred->security = new_domain; +					atomic_inc(&new_domain->users); +					atomic_dec(&old_domain->users); +					commit_creds(cred); +					error = 0; +				} +			} +		} +		tomoyo_read_unlock(idx); +	} else +		error = -EINVAL; +out: +	kfree(data); +	return error ? error : count; +} + +/** + * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface. + * + * @file:  Pointer to "struct file". + * @buf:   Domainname which current thread belongs to. + * @count: Size of @buf. + * @ppos:  Bytes read by now. + * + * Returns read size on success, negative value otherwise. + */ +static ssize_t tomoyo_read_self(struct file *file, char __user *buf, +				size_t count, loff_t *ppos) +{ +	const char *domain = tomoyo_domain()->domainname->name; +	loff_t len = strlen(domain); +	loff_t pos = *ppos; +	if (pos >= len || !count) +		return 0; +	len -= pos; +	if (count < len) +		len = count; +	if (copy_to_user(buf, domain + pos, len)) +		return -EFAULT; +	*ppos += len; +	return len; +} + +/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */ +static const struct file_operations tomoyo_self_operations = { +	.write = tomoyo_write_self, +	.read  = tomoyo_read_self, +}; + +/**   * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.   *   * @inode: Pointer to "struct inode". @@ -19,7 +135,7 @@   */  static int tomoyo_open(struct inode *inode, struct file *file)  { -	const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) +	const int key = ((u8 *) file_inode(file)->i_private)  		- ((u8 *) NULL);  	return tomoyo_open_control(key, file);  } @@ -27,23 +143,23 @@ static int tomoyo_open(struct inode *inode, struct file *file)  /**   * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.   * - * @inode: Pointer to "struct inode".   * @file:  Pointer to "struct file".   * - * Returns 0 on success, negative value otherwise.   */  static int tomoyo_release(struct inode *inode, struct file *file)  { -	return tomoyo_close_control(file); +	tomoyo_close_control(file->private_data); +	return 0;  }  /** - * tomoyo_poll - poll() for /proc/ccs/ interface. + * tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.   *   * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". + * @wait: Pointer to "poll_table". Maybe NULL.   * - * Returns 0 on success, negative value otherwise. + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, + * POLLOUT | POLLWRNORM otherwise.   */  static unsigned int tomoyo_poll(struct file *file, poll_table *wait)  { @@ -63,7 +179,7 @@ static unsigned int tomoyo_poll(struct file *file, poll_table *wait)  static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,  			   loff_t *ppos)  { -	return tomoyo_read_control(file, buf, count); +	return tomoyo_read_control(file->private_data, buf, count);  }  /** @@ -79,7 +195,7 @@ static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,  static ssize_t tomoyo_write(struct file *file, const char __user *buf,  			    size_t count, loff_t *ppos)  { -	return tomoyo_write_control(file, buf, count); +	return tomoyo_write_control(file->private_data, buf, count);  }  /* @@ -108,7 +224,7 @@ static const struct file_operations tomoyo_operations = {   *   * Returns nothing.   */ -static void __init tomoyo_create_entry(const char *name, const mode_t mode, +static void __init tomoyo_create_entry(const char *name, const umode_t mode,  				       struct dentry *parent, const u8 key)  {  	securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, @@ -135,20 +251,21 @@ static int __init tomoyo_initerface_init(void)  			    TOMOYO_DOMAINPOLICY);  	tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,  			    TOMOYO_EXCEPTIONPOLICY); -	tomoyo_create_entry("self_domain",      0400, tomoyo_dir, -			    TOMOYO_SELFDOMAIN); -	tomoyo_create_entry(".domain_status",   0600, tomoyo_dir, -			    TOMOYO_DOMAIN_STATUS); +	tomoyo_create_entry("audit",            0400, tomoyo_dir, +			    TOMOYO_AUDIT);  	tomoyo_create_entry(".process_status",  0600, tomoyo_dir,  			    TOMOYO_PROCESS_STATUS); -	tomoyo_create_entry("meminfo",          0600, tomoyo_dir, -			    TOMOYO_MEMINFO); +	tomoyo_create_entry("stat",             0644, tomoyo_dir, +			    TOMOYO_STAT);  	tomoyo_create_entry("profile",          0600, tomoyo_dir,  			    TOMOYO_PROFILE);  	tomoyo_create_entry("manager",          0600, tomoyo_dir,  			    TOMOYO_MANAGER);  	tomoyo_create_entry("version",          0400, tomoyo_dir,  			    TOMOYO_VERSION); +	securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL, +			       &tomoyo_self_operations); +	tomoyo_load_builtin_policy();  	return 0;  } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 95d3f957223..f0b756e27fe 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -1,20 +1,35 @@  /*   * security/tomoyo/tomoyo.c   * - * LSM hooks for TOMOYO Linux. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/security.h>  #include "common.h" +/** + * tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank(). + * + * @new: Pointer to "struct cred". + * @gfp: Memory allocation flags. + * + * Returns 0. + */  static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)  {  	new->security = NULL;  	return 0;  } +/** + * tomoyo_cred_prepare - Target for security_prepare_creds(). + * + * @new: Pointer to "struct cred". + * @old: Pointer to "struct cred". + * @gfp: Memory allocation flags. + * + * Returns 0. + */  static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,  			       gfp_t gfp)  { @@ -25,11 +40,22 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,  	return 0;  } +/** + * tomoyo_cred_transfer - Target for security_transfer_creds(). + * + * @new: Pointer to "struct cred". + * @old: Pointer to "struct cred". + */  static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)  {  	tomoyo_cred_prepare(new, old, 0);  } +/** + * tomoyo_cred_free - Target for security_cred_free(). + * + * @cred: Pointer to "struct cred". + */  static void tomoyo_cred_free(struct cred *cred)  {  	struct tomoyo_domain_info *domain = cred->security; @@ -37,6 +63,13 @@ static void tomoyo_cred_free(struct cred *cred)  		atomic_dec(&domain->users);  } +/** + * tomoyo_bprm_set_creds - Target for security_bprm_set_creds(). + * + * @bprm: Pointer to "struct linux_binprm". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)  {  	int rc; @@ -51,12 +84,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)  	 */  	if (bprm->cred_prepared)  		return 0; +#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER  	/*  	 * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested  	 * for the first time.  	 */  	if (!tomoyo_policy_loaded)  		tomoyo_load_policy(bprm->filename); +#endif  	/*  	 * Release reference to "struct tomoyo_domain_info" stored inside  	 * "bprm->cred->security". New reference to "struct tomoyo_domain_info" @@ -73,6 +108,13 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)  	return 0;  } +/** + * tomoyo_bprm_check_security - Target for security_bprm_check(). + * + * @bprm: Pointer to "struct linux_binprm". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_bprm_check_security(struct linux_binprm *bprm)  {  	struct tomoyo_domain_info *domain = bprm->cred->security; @@ -90,43 +132,109 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)  	/*  	 * Read permission is checked against interpreters using next domain.  	 */ -	return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); +	return tomoyo_check_open_permission(domain, &bprm->file->f_path, +					    O_RDONLY);  } +/** + * tomoyo_inode_getattr - Target for security_inode_getattr(). + * + * @mnt:    Pointer to "struct vfsmount". + * @dentry: Pointer to "struct dentry". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) +{ +	struct path path = { mnt, dentry }; +	return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, &path, NULL); +} + +/** + * tomoyo_path_truncate - Target for security_path_truncate(). + * + * @path: Pointer to "struct path". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_truncate(struct path *path)  { -	return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); +	return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);  } +/** + * tomoyo_path_unlink - Target for security_path_unlink(). + * + * @parent: Pointer to "struct path". + * @dentry: Pointer to "struct dentry". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)  {  	struct path path = { parent->mnt, dentry }; -	return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path); +	return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);  } +/** + * tomoyo_path_mkdir - Target for security_path_mkdir(). + * + * @parent: Pointer to "struct path". + * @dentry: Pointer to "struct dentry". + * @mode:   DAC permission mode. + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, -			     int mode) +			     umode_t mode)  {  	struct path path = { parent->mnt, dentry };  	return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,  				       mode & S_IALLUGO);  } +/** + * tomoyo_path_rmdir - Target for security_path_rmdir(). + * + * @parent: Pointer to "struct path". + * @dentry: Pointer to "struct dentry". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)  {  	struct path path = { parent->mnt, dentry }; -	return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path); +	return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);  } +/** + * tomoyo_path_symlink - Target for security_path_symlink(). + * + * @parent:   Pointer to "struct path". + * @dentry:   Pointer to "struct dentry". + * @old_name: Symlink's content. + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,  			       const char *old_name)  {  	struct path path = { parent->mnt, dentry }; -	return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path); +	return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);  } +/** + * tomoyo_path_mknod - Target for security_path_mknod(). + * + * @parent: Pointer to "struct path". + * @dentry: Pointer to "struct dentry". + * @mode:   DAC permission mode. + * @dev:    Device attributes. + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, -			     int mode, unsigned int dev) +			     umode_t mode, unsigned int dev)  {  	struct path path = { parent->mnt, dentry };  	int type = TOMOYO_TYPE_CREATE; @@ -155,6 +263,15 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,  	return tomoyo_path_number_perm(type, &path, perm);  } +/** + * tomoyo_path_link - Target for security_path_link(). + * + * @old_dentry: Pointer to "struct dentry". + * @new_dir:    Pointer to "struct path". + * @new_dentry: Pointer to "struct dentry". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,  			    struct dentry *new_dentry)  { @@ -163,6 +280,16 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,  	return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);  } +/** + * tomoyo_path_rename - Target for security_path_rename(). + * + * @old_parent: Pointer to "struct path". + * @old_dentry: Pointer to "struct dentry". + * @new_parent: Pointer to "struct path". + * @new_dentry: Pointer to "struct dentry". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_rename(struct path *old_parent,  			      struct dentry *old_dentry,  			      struct path *new_parent, @@ -173,15 +300,33 @@ static int tomoyo_path_rename(struct path *old_parent,  	return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);  } +/** + * tomoyo_file_fcntl - Target for security_file_fcntl(). + * + * @file: Pointer to "struct file". + * @cmd:  Command for fcntl(). + * @arg:  Argument for @cmd. + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,  			     unsigned long arg)  { -	if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) -		return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path); -	return 0; +	if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))) +		return 0; +	return tomoyo_check_open_permission(tomoyo_domain(), &file->f_path, +					    O_WRONLY | (arg & O_APPEND));  } -static int tomoyo_dentry_open(struct file *f, const struct cred *cred) +/** + * tomoyo_file_open - Target for security_file_open(). + * + * @f:    Pointer to "struct file". + * @cred: Pointer to "struct cred". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_file_open(struct file *f, const struct cred *cred)  {  	int flags = f->f_flags;  	/* Don't check read permission here if called from do_execve(). */ @@ -190,52 +335,170 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)  	return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);  } +/** + * tomoyo_file_ioctl - Target for security_file_ioctl(). + * + * @file: Pointer to "struct file". + * @cmd:  Command for ioctl(). + * @arg:  Argument for @cmd. + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,  			     unsigned long arg)  {  	return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd);  } -static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, -			     mode_t mode) +/** + * tomoyo_path_chmod - Target for security_path_chmod(). + * + * @path: Pointer to "struct path". + * @mode: DAC permission mode. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_path_chmod(struct path *path, umode_t mode)  { -	struct path path = { mnt, dentry }; -	return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, &path, +	return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, path,  				       mode & S_IALLUGO);  } -static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) +/** + * tomoyo_path_chown - Target for security_path_chown(). + * + * @path: Pointer to "struct path". + * @uid:  Owner ID. + * @gid:  Group ID. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_path_chown(struct path *path, kuid_t uid, kgid_t gid)  {  	int error = 0; -	if (uid != (uid_t) -1) -		error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid); -	if (!error && gid != (gid_t) -1) -		error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid); +	if (uid_valid(uid)) +		error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, +						from_kuid(&init_user_ns, uid)); +	if (!error && gid_valid(gid)) +		error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, +						from_kgid(&init_user_ns, gid));  	return error;  } +/** + * tomoyo_path_chroot - Target for security_path_chroot(). + * + * @path: Pointer to "struct path". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_path_chroot(struct path *path)  { -	return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path); +	return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);  } -static int tomoyo_sb_mount(char *dev_name, struct path *path, -			   char *type, unsigned long flags, void *data) +/** + * tomoyo_sb_mount - Target for security_sb_mount(). + * + * @dev_name: Name of device file. Maybe NULL. + * @path:     Pointer to "struct path". + * @type:     Name of filesystem type. Maybe NULL. + * @flags:    Mount options. + * @data:     Optional data. Maybe NULL. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_sb_mount(const char *dev_name, struct path *path, +			   const char *type, unsigned long flags, void *data)  {  	return tomoyo_mount_permission(dev_name, path, type, flags, data);  } +/** + * tomoyo_sb_umount - Target for security_sb_umount(). + * + * @mnt:   Pointer to "struct vfsmount". + * @flags: Unmount options. + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)  {  	struct path path = { mnt, mnt->mnt_root }; -	return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path); +	return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);  } +/** + * tomoyo_sb_pivotroot - Target for security_sb_pivotroot(). + * + * @old_path: Pointer to "struct path". + * @new_path: Pointer to "struct path". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)  {  	return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);  } +/** + * tomoyo_socket_listen - Check permission for listen(). + * + * @sock:    Pointer to "struct socket". + * @backlog: Backlog parameter. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_listen(struct socket *sock, int backlog) +{ +	return tomoyo_socket_listen_permission(sock); +} + +/** + * tomoyo_socket_connect - Check permission for connect(). + * + * @sock:     Pointer to "struct socket". + * @addr:     Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr, +				 int addr_len) +{ +	return tomoyo_socket_connect_permission(sock, addr, addr_len); +} + +/** + * tomoyo_socket_bind - Check permission for bind(). + * + * @sock:     Pointer to "struct socket". + * @addr:     Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr, +			      int addr_len) +{ +	return tomoyo_socket_bind_permission(sock, addr, addr_len); +} + +/** + * tomoyo_socket_sendmsg - Check permission for sendmsg(). + * + * @sock: Pointer to "struct socket". + * @msg:  Pointer to "struct msghdr". + * @size: Size of message. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, +				 int size) +{ +	return tomoyo_socket_sendmsg_permission(sock, msg, size); +} +  /*   * tomoyo_security_ops is a "struct security_operations" which is used for   * registering TOMOYO. @@ -249,7 +512,7 @@ static struct security_operations tomoyo_security_ops = {  	.bprm_set_creds      = tomoyo_bprm_set_creds,  	.bprm_check_security = tomoyo_bprm_check_security,  	.file_fcntl          = tomoyo_file_fcntl, -	.dentry_open         = tomoyo_dentry_open, +	.file_open           = tomoyo_file_open,  	.path_truncate       = tomoyo_path_truncate,  	.path_unlink         = tomoyo_path_unlink,  	.path_mkdir          = tomoyo_path_mkdir, @@ -258,6 +521,7 @@ static struct security_operations tomoyo_security_ops = {  	.path_mknod          = tomoyo_path_mknod,  	.path_link           = tomoyo_path_link,  	.path_rename         = tomoyo_path_rename, +	.inode_getattr       = tomoyo_inode_getattr,  	.file_ioctl          = tomoyo_file_ioctl,  	.path_chmod          = tomoyo_path_chmod,  	.path_chown          = tomoyo_path_chown, @@ -265,11 +529,20 @@ static struct security_operations tomoyo_security_ops = {  	.sb_mount            = tomoyo_sb_mount,  	.sb_umount           = tomoyo_sb_umount,  	.sb_pivotroot        = tomoyo_sb_pivotroot, +	.socket_bind         = tomoyo_socket_bind, +	.socket_connect      = tomoyo_socket_connect, +	.socket_listen       = tomoyo_socket_listen, +	.socket_sendmsg      = tomoyo_socket_sendmsg,  };  /* Lock for GC. */ -struct srcu_struct tomoyo_ss; +DEFINE_SRCU(tomoyo_ss); +/** + * tomoyo_init - Register TOMOYO Linux as a LSM module. + * + * Returns 0. + */  static int __init tomoyo_init(void)  {  	struct cred *cred = (struct cred *) current_cred(); @@ -277,8 +550,7 @@ static int __init tomoyo_init(void)  	if (!security_module_enable(&tomoyo_security_ops))  		return 0;  	/* register ourselves with the security framework */ -	if (register_security(&tomoyo_security_ops) || -	    init_srcu_struct(&tomoyo_ss)) +	if (register_security(&tomoyo_security_ops))  		panic("Failure registering TOMOYO Linux");  	printk(KERN_INFO "TOMOYO Linux initialized\n");  	cred->security = &tomoyo_kernel_domain; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 9bfc1ee8222..2952ba576fb 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -1,9 +1,7 @@  /*   * security/tomoyo/util.c   * - * Utility functions for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/slab.h> @@ -15,18 +13,188 @@ DEFINE_MUTEX(tomoyo_policy_lock);  /* Has /sbin/init started? */  bool tomoyo_policy_loaded; +/* + * Mapping table from "enum tomoyo_mac_index" to + * "enum tomoyo_mac_category_index". + */ +const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = { +	/* CONFIG::file group */ +	[TOMOYO_MAC_FILE_EXECUTE]    = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_OPEN]       = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_CREATE]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_UNLINK]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_GETATTR]    = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_MKDIR]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_RMDIR]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_MKFIFO]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_MKSOCK]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_TRUNCATE]   = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_SYMLINK]    = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_MKBLOCK]    = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_MKCHAR]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_LINK]       = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_RENAME]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_CHMOD]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_CHOWN]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_CHGRP]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_IOCTL]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_CHROOT]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_MOUNT]      = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_UMOUNT]     = TOMOYO_MAC_CATEGORY_FILE, +	[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, +	/* CONFIG::network group */ +	[TOMOYO_MAC_NETWORK_INET_STREAM_BIND]       = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN]     = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT]    = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND]        = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND]        = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_INET_RAW_BIND]          = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_INET_RAW_SEND]          = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND]       = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN]     = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT]    = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND]        = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND]        = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND]    = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN]  = +	TOMOYO_MAC_CATEGORY_NETWORK, +	[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = +	TOMOYO_MAC_CATEGORY_NETWORK, +	/* CONFIG::misc group */ +	[TOMOYO_MAC_ENVIRON]         = TOMOYO_MAC_CATEGORY_MISC, +}; + +/** + * tomoyo_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. + * + * @time:  Seconds since 1970/01/01 00:00:00. + * @stamp: Pointer to "struct tomoyo_time". + * + * Returns nothing. + * + * This function does not handle Y2038 problem. + */ +void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp) +{ +	static const u16 tomoyo_eom[2][12] = { +		{ 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, +		{ 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +	}; +	u16 y; +	u8 m; +	bool r; +	stamp->sec = time % 60; +	time /= 60; +	stamp->min = time % 60; +	time /= 60; +	stamp->hour = time % 24; +	time /= 24; +	for (y = 1970; ; y++) { +		const unsigned short days = (y & 3) ? 365 : 366; +		if (time < days) +			break; +		time -= days; +	} +	r = (y & 3) == 0; +	for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++) +		; +	if (m) +		time -= tomoyo_eom[r][m - 1]; +	stamp->year = y; +	stamp->month = ++m; +	stamp->day = ++time; +} + +/** + * tomoyo_permstr - Find permission keywords. + * + * @string: String representation for permissions in foo/bar/buz format. + * @keyword: Keyword to find from @string/ + * + * Returns ture if @keyword was found in @string, false otherwise. + * + * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2. + */ +bool tomoyo_permstr(const char *string, const char *keyword) +{ +	const char *cp = strstr(string, keyword); +	if (cp) +		return cp == string || *(cp - 1) == '/'; +	return false; +} + +/** + * tomoyo_read_token - Read a word from a line. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns a word on success, "" otherwise. + * + * To allow the caller to skip NULL check, this function returns "" rather than + * NULL if there is no more words to read. + */ +char *tomoyo_read_token(struct tomoyo_acl_param *param) +{ +	char *pos = param->data; +	char *del = strchr(pos, ' '); +	if (del) +		*del++ = '\0'; +	else +		del = pos + strlen(pos); +	param->data = del; +	return pos; +} + +/** + * tomoyo_get_domainname - Read a domainname from a line. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns a domainname on success, NULL otherwise. + */ +const struct tomoyo_path_info *tomoyo_get_domainname +(struct tomoyo_acl_param *param) +{ +	char *start = param->data; +	char *pos = start; +	while (*pos) { +		if (*pos++ != ' ' || *pos++ == '/') +			continue; +		pos -= 2; +		*pos++ = '\0'; +		break; +	} +	param->data = pos; +	if (tomoyo_correct_domain(start)) +		return tomoyo_get_name(start); +	return NULL; +} +  /**   * tomoyo_parse_ulong - Parse an "unsigned long" value.   *   * @result: Pointer to "unsigned long".   * @str:    Pointer to string to parse.   * - * Returns value type on success, 0 otherwise. + * Returns one of values in "enum tomoyo_value_type".   *   * The @src is updated to point the first character after the value   * on success.   */ -static u8 tomoyo_parse_ulong(unsigned long *result, char **str) +u8 tomoyo_parse_ulong(unsigned long *result, char **str)  {  	const char *cp = *str;  	char *ep; @@ -43,7 +211,7 @@ static u8 tomoyo_parse_ulong(unsigned long *result, char **str)  	}  	*result = simple_strtoul(cp, &ep, base);  	if (cp == ep) -		return 0; +		return TOMOYO_VALUE_TYPE_INVALID;  	*str = ep;  	switch (base) {  	case 16: @@ -81,63 +249,65 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len,  /**   * tomoyo_parse_name_union - Parse a tomoyo_name_union.   * - * @filename: Name or name group. - * @ptr:      Pointer to "struct tomoyo_name_union". + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr:   Pointer to "struct tomoyo_name_union".   *   * Returns true on success, false otherwise.   */ -bool tomoyo_parse_name_union(const char *filename, +bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,  			     struct tomoyo_name_union *ptr)  { -	if (!tomoyo_correct_word(filename)) -		return false; -	if (filename[0] == '@') { -		ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP); -		ptr->is_group = true; +	char *filename; +	if (param->data[0] == '@') { +		param->data++; +		ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);  		return ptr->group != NULL;  	} +	filename = tomoyo_read_token(param); +	if (!tomoyo_correct_word(filename)) +		return false;  	ptr->filename = tomoyo_get_name(filename); -	ptr->is_group = false;  	return ptr->filename != NULL;  }  /**   * tomoyo_parse_number_union - Parse a tomoyo_number_union.   * - * @data: Number or number range or number group. - * @ptr:  Pointer to "struct tomoyo_number_union". + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr:   Pointer to "struct tomoyo_number_union".   *   * Returns true on success, false otherwise.   */ -bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) +bool tomoyo_parse_number_union(struct tomoyo_acl_param *param, +			       struct tomoyo_number_union *ptr)  { +	char *data;  	u8 type;  	unsigned long v; -	memset(num, 0, sizeof(*num)); -	if (data[0] == '@') { -		if (!tomoyo_correct_word(data)) -			return false; -		num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP); -		num->is_group = true; -		return num->group != NULL; +	memset(ptr, 0, sizeof(*ptr)); +	if (param->data[0] == '@') { +		param->data++; +		ptr->group = tomoyo_get_group(param, TOMOYO_NUMBER_GROUP); +		return ptr->group != NULL;  	} +	data = tomoyo_read_token(param);  	type = tomoyo_parse_ulong(&v, &data); -	if (!type) +	if (type == TOMOYO_VALUE_TYPE_INVALID)  		return false; -	num->values[0] = v; -	num->min_type = type; +	ptr->values[0] = v; +	ptr->value_type[0] = type;  	if (!*data) { -		num->values[1] = v; -		num->max_type = type; +		ptr->values[1] = v; +		ptr->value_type[1] = type;  		return true;  	}  	if (*data++ != '-')  		return false;  	type = tomoyo_parse_ulong(&v, &data); -	if (!type || *data) +	if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)  		return false; -	num->values[1] = v; -	num->max_type = type; +	ptr->values[1] = v; +	ptr->value_type[1] = type;  	return true;  } @@ -185,6 +355,30 @@ static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)  }  /** + * tomoyo_valid - Check whether the character is a valid char. + * + * @c: The character to check. + * + * Returns true if @c is a valid character, false otherwise. + */ +static inline bool tomoyo_valid(const unsigned char c) +{ +	return c > ' ' && c < 127; +} + +/** + * tomoyo_invalid - Check whether the character is an invalid char. + * + * @c: The character to check. + * + * Returns true if @c is an invalid character, false otherwise. + */ +static inline bool tomoyo_invalid(const unsigned char c) +{ +	return c && (c <= ' ' || c >= 127); +} + +/**   * tomoyo_str_starts - Check whether the given string starts with the given keyword.   *   * @src:  Pointer to pointer to the string. @@ -238,36 +432,9 @@ void tomoyo_normalize_line(unsigned char *buffer)  }  /** - * tomoyo_tokenize - Tokenize string. - * - * @buffer: The line to tokenize. - * @w:      Pointer to "char *". - * @size:   Sizeof @w . - * - * Returns true on success, false otherwise. - */ -bool tomoyo_tokenize(char *buffer, char *w[], size_t size) -{ -	int count = size / sizeof(char *); -	int i; -	for (i = 0; i < count; i++) -		w[i] = ""; -	for (i = 0; i < count; i++) { -		char *cp = strchr(buffer, ' '); -		if (cp) -			*cp = '\0'; -		w[i] = buffer; -		if (!cp) -			break; -		buffer = cp + 1; -	} -	return i < count || !*buffer; -} - -/**   * tomoyo_correct_word2 - Validate a string.   * - * @string: The string to check. May be non-'\0'-terminated. + * @string: The string to check. Maybe non-'\0'-terminated.   * @len:    Length of @string.   *   * Check whether the given string follows the naming rules. @@ -325,13 +492,13 @@ static bool tomoyo_correct_word2(const char *string, size_t len)  				if (d < '0' || d > '7' || e < '0' || e > '7')  					break;  				c = tomoyo_make_byte(c, d, e); -				if (tomoyo_invalid(c)) -					continue; /* pattern is not \000 */ +				if (c <= ' ' || c >= 127) +					continue;  			}  			goto out;  		} else if (in_repetition && c == '/') {  			goto out; -		} else if (tomoyo_invalid(c)) { +		} else if (c <= ' ' || c >= 127) {  			goto out;  		}  	} @@ -377,26 +544,21 @@ bool tomoyo_correct_path(const char *filename)   */  bool tomoyo_correct_domain(const unsigned char *domainname)  { -	if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, -				   TOMOYO_ROOT_NAME_LEN)) -		goto out; -	domainname += TOMOYO_ROOT_NAME_LEN; -	if (!*domainname) +	if (!domainname || !tomoyo_domain_def(domainname)) +		return false; +	domainname = strchr(domainname, ' '); +	if (!domainname++)  		return true; -	if (*domainname++ != ' ') -		goto out;  	while (1) {  		const unsigned char *cp = strchr(domainname, ' ');  		if (!cp)  			break;  		if (*domainname != '/' || -		    !tomoyo_correct_word2(domainname, cp - domainname - 1)) -			goto out; +		    !tomoyo_correct_word2(domainname, cp - domainname)) +			return false;  		domainname = cp + 1;  	}  	return tomoyo_correct_path(domainname); - out: -	return false;  }  /** @@ -408,7 +570,19 @@ bool tomoyo_correct_domain(const unsigned char *domainname)   */  bool tomoyo_domain_def(const unsigned char *buffer)  { -	return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); +	const unsigned char *cp; +	int len; +	if (*buffer != '<') +		return false; +	cp = strchr(buffer, ' '); +	if (!cp) +		len = strlen(buffer); +	else +		len = cp - buffer; +	if (buffer[len - 1] != '>' || +	    !tomoyo_correct_word2(buffer + 1, len - 2)) +		return false; +	return true;  }  /** @@ -775,18 +949,13 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,  const char *tomoyo_get_exe(void)  {  	struct mm_struct *mm = current->mm; -	struct vm_area_struct *vma;  	const char *cp = NULL;  	if (!mm)  		return NULL;  	down_read(&mm->mmap_sem); -	for (vma = mm->mmap; vma; vma = vma->vm_next) { -		if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { -			cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); -			break; -		} -	} +	if (mm->exe_file) +		cp = tomoyo_realpath_from_path(&mm->exe_file->f_path);  	up_read(&mm->mmap_sem);  	return cp;  } @@ -794,22 +963,27 @@ const char *tomoyo_get_exe(void)  /**   * tomoyo_get_mode - Get MAC mode.   * + * @ns:      Pointer to "struct tomoyo_policy_namespace".   * @profile: Profile number.   * @index:   Index number of functionality.   *   * Returns mode.   */ -int tomoyo_get_mode(const u8 profile, const u8 index) +int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, +		    const u8 index)  {  	u8 mode; -	const u8 category = TOMOYO_MAC_CATEGORY_FILE; +	struct tomoyo_profile *p; +  	if (!tomoyo_policy_loaded)  		return TOMOYO_CONFIG_DISABLED; -	mode = tomoyo_profile(profile)->config[index]; +	p = tomoyo_profile(ns, profile); +	mode = p->config[index];  	if (mode == TOMOYO_CONFIG_USE_DEFAULT) -		mode = tomoyo_profile(profile)->config[category]; +		mode = p->config[tomoyo_index2category[index] +				 + TOMOYO_MAX_MAC_INDEX];  	if (mode == TOMOYO_CONFIG_USE_DEFAULT) -		mode = tomoyo_profile(profile)->default_config; +		mode = p->default_config;  	return mode & 3;  } @@ -833,65 +1007,11 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r,  	profile = domain->profile;  	r->profile = profile;  	r->type = index; -	r->mode = tomoyo_get_mode(profile, index); +	r->mode = tomoyo_get_mode(domain->ns, profile, index);  	return r->mode;  }  /** - * tomoyo_last_word - Get last component of a line. - * - * @line: A line. - * - * Returns the last word of a line. - */ -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". - * @fmt: The printf()'s format string, followed by parameters. - */ -void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) -{ -	va_list args; -	char *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; -		break; -	case TOMOYO_CONFIG_PERMISSIVE: -		if (!profile->permissive->permissive_verbose) -			return; -		break; -	case TOMOYO_CONFIG_LEARNING: -		if (!profile->learning->learning_verbose) -			return; -		break; -	} -	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); -} - -/**   * tomoyo_domain_quota_is_ok - Check for domain's quota.   *   * @r: Pointer to "struct tomoyo_request_info". @@ -911,52 +1031,54 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)  	if (!domain)  		return true;  	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { +		u16 perm; +		u8 i;  		if (ptr->is_deleted)  			continue;  		switch (ptr->type) { -			u16 perm; -			u8 i;  		case TOMOYO_TYPE_PATH_ACL:  			perm = container_of(ptr, struct tomoyo_path_acl, head)  				->perm; -			for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) -				if (perm & (1 << i)) -					count++; -			if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) -				count -= 2;  			break;  		case TOMOYO_TYPE_PATH2_ACL:  			perm = container_of(ptr, struct tomoyo_path2_acl, head)  				->perm; -			for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) -				if (perm & (1 << i)) -					count++;  			break;  		case TOMOYO_TYPE_PATH_NUMBER_ACL:  			perm = container_of(ptr, struct tomoyo_path_number_acl,  					    head)->perm; -			for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) -				if (perm & (1 << i)) -					count++;  			break;  		case TOMOYO_TYPE_MKDEV_ACL:  			perm = container_of(ptr, struct tomoyo_mkdev_acl,  					    head)->perm; -			for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++) -				if (perm & (1 << i)) -					count++; +			break; +		case TOMOYO_TYPE_INET_ACL: +			perm = container_of(ptr, struct tomoyo_inet_acl, +					    head)->perm; +			break; +		case TOMOYO_TYPE_UNIX_ACL: +			perm = container_of(ptr, struct tomoyo_unix_acl, +					    head)->perm; +			break; +		case TOMOYO_TYPE_MANUAL_TASK_ACL: +			perm = 0;  			break;  		default: -			count++; +			perm = 1;  		} +		for (i = 0; i < 16; i++) +			if (perm & (1 << i)) +				count++;  	} -	if (count < tomoyo_profile(domain->profile)->learning-> -	    learning_max_entry) +	if (count < tomoyo_profile(domain->ns, domain->profile)-> +	    pref[TOMOYO_PREF_MAX_LEARNING_ENTRY])  		return true; -	if (!domain->quota_warned) { -		domain->quota_warned = true; -		printk(KERN_WARNING "TOMOYO-WARNING: " -		       "Domain '%s' has so many ACLs to hold. " +	if (!domain->flags[TOMOYO_DIF_QUOTA_WARNED]) { +		domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true; +		/* r->granted = false; */ +		tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]); +		printk(KERN_WARNING "WARNING: " +		       "Domain '%s' has too many ACLs to hold. "  		       "Stopped learning mode.\n", domain->domainname->name);  	}  	return false;  | 
