diff options
Diffstat (limited to 'security/tomoyo/domain.c')
| -rw-r--r-- | security/tomoyo/domain.c | 853 | 
1 files changed, 295 insertions, 558 deletions
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd8ba444676..35388408e47 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -1,12 +1,9 @@  /*   * security/tomoyo/domain.c   * - * Implementation of the Domain-Based Mandatory Access Control. - * - * Copyright (C) 2005-2009  NTT DATA CORPORATION - * - * Version: 2.2.0   2009/04/01 + * Domain transition functions for TOMOYO.   * + * Copyright (C) 2005-2010  NTT DATA CORPORATION   */  #include "common.h" @@ -18,366 +15,191 @@  /* The initial domain. */  struct tomoyo_domain_info tomoyo_kernel_domain; -/* - * tomoyo_domain_list is used for holding list of domains. - * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding - * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain. - * - * An entry is added by - * - * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \ - *                                  /sys/kernel/security/tomoyo/domain_policy - * - * and is deleted by - * - * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \ - *                                  /sys/kernel/security/tomoyo/domain_policy - * - * and all entries are retrieved by - * - * # cat /sys/kernel/security/tomoyo/domain_policy - * - * A domain is added by - * - * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy - * - * and is deleted by - * - * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy - * - * and all domains are retrieved by - * - * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy - * - * Normally, a domainname is monotonically getting longer because a domainname - * which the process will belong to if an execve() operation succeeds is - * defined as a concatenation of "current domainname" + "pathname passed to - * execve()". - * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for - * exceptions. - */ -LIST_HEAD(tomoyo_domain_list); -  /** - * tomoyo_get_last_name - Get last component of a domainname. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * - * Returns the last component of the domainname. - */ -const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) -{ -	const char *cp0 = domain->domainname->name; -	const char *cp1 = strrchr(cp0, ' '); - -	if (cp1) -		return cp1 + 1; -	return cp0; -} - -/* - * tomoyo_domain_initializer_list is used for holding list of programs which - * triggers reinitialization of domainname. Normally, a domainname is - * monotonically getting longer. But sometimes, we restart daemon programs. - * It would be convenient for us that "a daemon started upon system boot" and - * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO - * provides a way to shorten domainnames. + * tomoyo_update_policy - Update an entry for exception policy.   * - * An entry is added by - * - * # echo 'initialize_domain /usr/sbin/httpd' > \ - *                               /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete initialize_domain /usr/sbin/httpd' > \ - *                               /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, /usr/sbin/httpd will belong to - * "<kernel> /usr/sbin/httpd" domain. - * - * You may specify a domainname using "from" keyword. - * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" - * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd" - * domain to belong to "<kernel> /usr/sbin/httpd" domain. - * - * You may add "no_" prefix to "initialize_domain". - * "initialize_domain /usr/sbin/httpd" and - * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" - * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain - * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain. - */ -LIST_HEAD(tomoyo_domain_initializer_list); - -/** - * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. - * - * @domainname: The name of domain. May be NULL. - * @program:    The name of program. - * @is_not:     True if it is "no_initialize_domain" entry. - * @is_delete:  True if it is a delete request. + * @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". + * @check_duplicate: Callback function to find duplicated entry.   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_update_domain_initializer_entry(const char *domainname, -						  const char *program, -						  const bool is_not, -						  const bool is_delete) +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_domain_initializer_entry *ptr; -	struct tomoyo_domain_initializer_entry e = { .is_not = is_not };  	int error = is_delete ? -ENOENT : -ENOMEM; +	struct tomoyo_acl_head *entry; -	if (!tomoyo_is_correct_path(program, 1, -1, -1)) -		return -EINVAL; /* No patterns allowed. */ -	if (domainname) { -		if (!tomoyo_is_domain_def(domainname) && -		    tomoyo_is_correct_path(domainname, 1, -1, -1)) -			e.is_last_name = true; -		else if (!tomoyo_is_correct_domain(domainname)) -			return -EINVAL; -		e.domainname = tomoyo_get_name(domainname); -		if (!e.domainname) -			goto out; -	} -	e.program = tomoyo_get_name(program); -	if (!e.program) -		goto out;  	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		goto out; -	list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { -		if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) +		return -ENOMEM; +	list_for_each_entry_rcu(entry, list, list) { +		if (!check_duplicate(entry, new_entry))  			continue; -		ptr->is_deleted = is_delete; +		entry->is_deleted = is_delete;  		error = 0;  		break;  	} -	if (!is_delete && error) { -		struct tomoyo_domain_initializer_entry *entry = -			tomoyo_commit_ok(&e, sizeof(e)); +	if (error && !is_delete) { +		entry = tomoyo_commit_ok(new_entry, size);  		if (entry) { -			list_add_tail_rcu(&entry->list, -					  &tomoyo_domain_initializer_list); +			list_add_tail_rcu(&entry->list, list);  			error = 0;  		}  	}  	mutex_unlock(&tomoyo_policy_lock); - out: -	tomoyo_put_name(e.domainname); -	tomoyo_put_name(e.program);  	return error;  }  /** - * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list. + * tomoyo_update_domain - Update an entry for domain policy.   * - * @head: Pointer to "struct tomoyo_io_buffer". + * @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". + * @check_duplicate: Callback function to find duplicated entry. + * @merge_duplicate: Callback function to merge duplicated entry.   * - * Returns true on success, false otherwise. + * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) +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 list_head *pos; -	bool done = true; +	int error = is_delete ? -ENOENT : -ENOMEM; +	struct tomoyo_acl_info *entry; -	list_for_each_cookie(pos, head->read_var2, -			     &tomoyo_domain_initializer_list) { -		const char *no; -		const char *from = ""; -		const char *domain = ""; -		struct tomoyo_domain_initializer_entry *ptr; -		ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, -				  list); -		if (ptr->is_deleted) +	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))  			continue; -		no = ptr->is_not ? "no_" : ""; -		if (ptr->domainname) { -			from = " from "; -			domain = ptr->domainname->name; +		if (merge_duplicate) +			entry->is_deleted = merge_duplicate(entry, new_entry, +							    is_delete); +		else +			entry->is_deleted = is_delete; +		error = 0; +		break; +	} +	if (error && !is_delete) { +		entry = tomoyo_commit_ok(new_entry, size); +		if (entry) { +			list_add_tail_rcu(&entry->list, &domain->acl_info_list); +			error = 0;  		} -		done = tomoyo_io_printf(head, -					"%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN -					"%s%s%s\n", no, ptr->program->name, -					from, domain); -		if (!done) -			break;  	} -	return done; +	mutex_unlock(&tomoyo_policy_lock); +	return error;  } -/** - * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. - * - * @data:      String to parse. - * @is_not:    True if it is "no_initialize_domain" entry. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, -					   const bool is_delete) +void tomoyo_check_acl(struct tomoyo_request_info *r, +		      bool (*check_entry) (struct tomoyo_request_info *, +					   const struct tomoyo_acl_info *))  { -	char *cp = strstr(data, " from "); +	const struct tomoyo_domain_info *domain = r->domain; +	struct tomoyo_acl_info *ptr; -	if (cp) { -		*cp = '\0'; -		return tomoyo_update_domain_initializer_entry(cp + 6, data, -							      is_not, -							      is_delete); +	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { +		if (ptr->is_deleted || ptr->type != r->param_type) +			continue; +		if (check_entry(r, ptr)) { +			r->granted = true; +			return; +		}  	} -	return tomoyo_update_domain_initializer_entry(NULL, data, is_not, -						      is_delete); +	r->granted = false;  } +/* 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_is_domain_initializer - Check whether the given program causes domainname reinitialization. - * - * @domainname: The name of domain. - * @program:    The name of program. - * @last_name:  The last component of @domainname. + * tomoyo_last_word - Get last component of a domainname.   * - * Returns true if executing @program reinitializes domain transition, - * false otherwise. + * @domainname: Domainname to check.   * - * Caller holds tomoyo_read_lock(). + * Returns the last word of @domainname.   */ -static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * -					 domainname, -					 const struct tomoyo_path_info *program, -					 const struct tomoyo_path_info * -					 last_name) +static const char *tomoyo_last_word(const char *name)  { -	struct tomoyo_domain_initializer_entry *ptr; -	bool flag = false; - -	list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { -		if (ptr->is_deleted) -			continue; -		if (ptr->domainname) { -			if (!ptr->is_last_name) { -				if (ptr->domainname != domainname) -					continue; -			} else { -				if (tomoyo_pathcmp(ptr->domainname, last_name)) -					continue; -			} -		} -		if (tomoyo_pathcmp(ptr->program, program)) -			continue; -		if (ptr->is_not) { -			flag = false; -			break; -		} -		flag = true; -	} -	return flag; +        const char *cp = strrchr(name, ' '); +        if (cp) +                return cp + 1; +        return name;  } -/* - * tomoyo_domain_keeper_list is used for holding list of domainnames which - * suppresses domain transition. Normally, a domainname is monotonically - * getting longer. But sometimes, we want to suppress domain transition. - * It would be convenient for us that programs executed from a login session - * belong to the same domain. Thus, TOMOYO provides a way to suppress domain - * transition. - * - * An entry is added by - * - * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ - *                              /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ - *                              /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, any process which belongs to - * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain, - * unless explicitly specified by "initialize_domain" or "no_keep_domain". - * - * You may specify a program using "from" keyword. - * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash" - * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash" - * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain. - * - * You may add "no_" prefix to "keep_domain". - * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and - * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will - * cause "/usr/bin/passwd" to belong to - * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless - * explicitly specified by "initialize_domain". - */ -LIST_HEAD(tomoyo_domain_keeper_list); +static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, +					   const struct tomoyo_acl_head *b) +{ +	const struct tomoyo_transition_control *p1 = container_of(a, +								  typeof(*p1), +								  head); +	const struct tomoyo_transition_control *p2 = container_of(b, +								  typeof(*p2), +								  head); +	return p1->type == p2->type && p1->is_last_name == p2->is_last_name +		&& p1->domainname == p2->domainname +		&& p1->program == p2->program; +}  /** - * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. + * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.   * - * @domainname: The name of domain. - * @program:    The name of program. May be NULL. - * @is_not:     True if it is "no_keep_domain" entry. + * @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.   *   * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock().   */ -static int tomoyo_update_domain_keeper_entry(const char *domainname, -					     const char *program, -					     const bool is_not, -					     const bool is_delete) +static int tomoyo_update_transition_control_entry(const char *domainname, +						  const char *program, +						  const u8 type, +						  const bool is_delete)  { -	struct tomoyo_domain_keeper_entry *ptr; -	struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; +	struct tomoyo_transition_control e = { .type = type };  	int error = is_delete ? -ENOENT : -ENOMEM; - -	if (!tomoyo_is_domain_def(domainname) && -	    tomoyo_is_correct_path(domainname, 1, -1, -1)) -		e.is_last_name = true; -	else if (!tomoyo_is_correct_domain(domainname)) -		return -EINVAL;  	if (program) { -		if (!tomoyo_is_correct_path(program, 1, -1, -1)) +		if (!tomoyo_correct_path(program))  			return -EINVAL;  		e.program = tomoyo_get_name(program);  		if (!e.program)  			goto out;  	} -	e.domainname = tomoyo_get_name(domainname); -	if (!e.domainname) -		goto out; -	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		goto out; -	list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { -		if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) -			continue; -		ptr->is_deleted = is_delete; -		error = 0; -		break; -	} -	if (!is_delete && error) { -		struct tomoyo_domain_keeper_entry *entry = -			tomoyo_commit_ok(&e, sizeof(e)); -		if (entry) { -			list_add_tail_rcu(&entry->list, -					  &tomoyo_domain_keeper_list); -			error = 0; +	if (domainname) { +		if (!tomoyo_correct_domain(domainname)) { +			if (!tomoyo_correct_path(domainname)) +				goto out; +			e.is_last_name = true;  		} +		e.domainname = tomoyo_get_name(domainname); +		if (!e.domainname) +			goto out;  	} -	mutex_unlock(&tomoyo_policy_lock); +	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, +				     &tomoyo_policy_list +				     [TOMOYO_ID_TRANSITION_CONTROL], +				     tomoyo_same_transition_control);   out:  	tomoyo_put_name(e.domainname);  	tomoyo_put_name(e.program); @@ -385,219 +207,133 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,  }  /** - * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list. + * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.   *   * @data:      String to parse. - * @is_not:    True if it is "no_keep_domain" entry.   * @is_delete: True if it is a delete request. + * @type:      Type of this entry.   * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, -				      const bool is_delete) -{ -	char *cp = strstr(data, " from "); - -	if (cp) { -		*cp = '\0'; -		return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not, -							 is_delete); -	} -	return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete); -} - -/** - * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). + * Returns 0 on success, negative value otherwise.   */ -bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) +int tomoyo_write_transition_control(char *data, const bool is_delete, +				    const u8 type)  { -	struct list_head *pos; -	bool done = true; - -	list_for_each_cookie(pos, head->read_var2, -			     &tomoyo_domain_keeper_list) { -		struct tomoyo_domain_keeper_entry *ptr; -		const char *no; -		const char *from = ""; -		const char *program = ""; - -		ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list); -		if (ptr->is_deleted) -			continue; -		no = ptr->is_not ? "no_" : ""; -		if (ptr->program) { -			from = " from "; -			program = ptr->program->name; -		} -		done = tomoyo_io_printf(head, -					"%s" TOMOYO_KEYWORD_KEEP_DOMAIN -					"%s%s%s\n", no, program, from, -					ptr->domainname->name); -		if (!done) -			break; +	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;  	} -	return done; +	return tomoyo_update_transition_control_entry(domainname, data, type, +						      is_delete);  }  /** - * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression. + * tomoyo_transition_type - Get domain transition type.   *   * @domainname: The name of domain.   * @program:    The name of program. - * @last_name:  The last component of @domainname.   * - * Returns true if executing @program supresses domain transition, - * false otherwise. + * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program + * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing + * @program suppresses domain transition, others otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, -				    const struct tomoyo_path_info *program, -				    const struct tomoyo_path_info *last_name) +static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, +				 const struct tomoyo_path_info *program)  { -	struct tomoyo_domain_keeper_entry *ptr; -	bool flag = false; - -	list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { -		if (ptr->is_deleted) -			continue; -		if (!ptr->is_last_name) { -			if (ptr->domainname != domainname) +	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; -		} else { -			if (tomoyo_pathcmp(ptr->domainname, last_name)) +			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;  		} -		if (ptr->program && tomoyo_pathcmp(ptr->program, program)) -			continue; -		if (ptr->is_not) { -			flag = false; -			break; -		} -		flag = true;  	} -	return flag; + done: +	return type;  } -/* - * tomoyo_alias_list is used for holding list of symlink's pathnames which are - * allowed to be passed to an execve() request. Normally, the domainname which - * the current process will belong to after execve() succeeds is calculated - * using dereferenced pathnames. But some programs behave differently depending - * on the name passed to argv[0]. For busybox, calculating domainname using - * dereferenced pathnames will cause all programs in the busybox to belong to - * the same domain. Thus, TOMOYO provides a way to allow use of symlink's - * pathname for checking execve()'s permission and calculating domainname which - * the current process will belong to after execve() succeeds. - * - * An entry is added by - * - * # echo 'alias /bin/busybox /bin/cat' > \ - *                            /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete alias /bin/busybox /bin/cat' > \ - *                            /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^alias /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, if /bin/cat is a symlink to /bin/busybox and execution - * of /bin/cat is requested, permission is checked for /bin/cat rather than - * /bin/busybox and domainname which the current process will belong to after - * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . - */ -LIST_HEAD(tomoyo_alias_list); +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); +	return p1->original_name == p2->original_name && +		p1->aggregated_name == p2->aggregated_name; +}  /** - * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. + * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.   * - * @original_name: The original program's real name. - * @aliased_name:  The symbolic program's symbolic link's name. - * @is_delete:     True if it is a delete request. + * @original_name:   The original program's name. + * @aggregated_name: The program name to use. + * @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_alias_entry(const char *original_name, -				     const char *aliased_name, -				     const bool is_delete) +static int tomoyo_update_aggregator_entry(const char *original_name, +					  const char *aggregated_name, +					  const bool is_delete)  { -	struct tomoyo_alias_entry *ptr; -	struct tomoyo_alias_entry e = { }; +	struct tomoyo_aggregator e = { };  	int error = is_delete ? -ENOENT : -ENOMEM; -	if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || -	    !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) -		return -EINVAL; /* No patterns allowed. */ +	if (!tomoyo_correct_path(original_name) || +	    !tomoyo_correct_path(aggregated_name)) +		return -EINVAL;  	e.original_name = tomoyo_get_name(original_name); -	e.aliased_name = tomoyo_get_name(aliased_name); -	if (!e.original_name || !e.aliased_name) +	e.aggregated_name = tomoyo_get_name(aggregated_name); +	if (!e.original_name || !e.aggregated_name || +	    e.aggregated_name->is_patterned) /* No patterns allowed. */  		goto out; -	if (mutex_lock_interruptible(&tomoyo_policy_lock)) -		goto out; -	list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { -		if (!tomoyo_is_same_alias_entry(ptr, &e)) -			continue; -		ptr->is_deleted = is_delete; -		error = 0; -		break; -	} -	if (!is_delete && error) { -		struct tomoyo_alias_entry *entry = -			tomoyo_commit_ok(&e, sizeof(e)); -		if (entry) { -			list_add_tail_rcu(&entry->list, &tomoyo_alias_list); -			error = 0; -		} -	} -	mutex_unlock(&tomoyo_policy_lock); +	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, +				     &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], +				     tomoyo_same_aggregator);   out:  	tomoyo_put_name(e.original_name); -	tomoyo_put_name(e.aliased_name); +	tomoyo_put_name(e.aggregated_name);  	return error;  }  /** - * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) -{ -	struct list_head *pos; -	bool done = true; - -	list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { -		struct tomoyo_alias_entry *ptr; - -		ptr = list_entry(pos, struct tomoyo_alias_entry, list); -		if (ptr->is_deleted) -			continue; -		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n", -					ptr->original_name->name, -					ptr->aliased_name->name); -		if (!done) -			break; -	} -	return done; -} - -/** - * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list. + * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.   *   * @data:      String to parse.   * @is_delete: True if it is a delete request. @@ -606,18 +342,18 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)   *   * Caller holds tomoyo_read_lock().   */ -int tomoyo_write_alias_policy(char *data, const bool is_delete) +int tomoyo_write_aggregator(char *data, const bool is_delete)  {  	char *cp = strchr(data, ' ');  	if (!cp)  		return -EINVAL;  	*cp++ = '\0'; -	return tomoyo_update_alias_entry(data, cp, is_delete); +	return tomoyo_update_aggregator_entry(data, cp, is_delete);  }  /** - * tomoyo_find_or_assign_new_domain - Create a domain. + * tomoyo_assign_domain - Create a domain.   *   * @domainname: The name of domain.   * @profile:    Profile number to assign if the domain was newly created. @@ -626,16 +362,15 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)   *   * Caller holds tomoyo_read_lock().   */ -struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * -							    domainname, -							    const u8 profile) +struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, +						const u8 profile)  {  	struct tomoyo_domain_info *entry;  	struct tomoyo_domain_info *domain = NULL;  	const struct tomoyo_path_info *saved_domainname;  	bool found = false; -	if (!tomoyo_is_correct_domain(domainname)) +	if (!tomoyo_correct_domain(domainname))  		return NULL;  	saved_domainname = tomoyo_get_name(domainname);  	if (!saved_domainname) @@ -678,116 +413,118 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *   */  int tomoyo_find_next_domain(struct linux_binprm *bprm)  { -	/* -	 * This function assumes that the size of buffer returned by -	 * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. -	 */ -	struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS); +	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 *old_domain_name = old_domain->domainname->name;  	const char *original_name = bprm->filename; -	char *new_domain_name = NULL; -	char *real_program_name = NULL; -	char *symlink_program_name = NULL; -	const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); -	const bool is_enforce = (mode == 3); +	u8 mode; +	bool is_enforce;  	int retval = -ENOMEM; -	struct tomoyo_path_info r; /* real name */ -	struct tomoyo_path_info s; /* symlink name */ -	struct tomoyo_path_info l; /* last name */ -	static bool initialized; +	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; -	if (!initialized) { -		/* -		 * Built-in initializers. This is needed because policies are -		 * not loaded until starting /sbin/init. -		 */ -		tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug", -						       false, false); -		tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe", -						       false, false); -		initialized = true; + retry: +	if (need_kfree) { +		kfree(rn.name); +		need_kfree = false;  	} - -	/* Get tomoyo_realpath of program. */ +	/* Get symlink's pathname of program. */  	retval = -ENOENT; -	/* I hope tomoyo_realpath() won't fail with -ENOMEM. */ -	real_program_name = tomoyo_realpath(original_name); -	if (!real_program_name) -		goto out; -	/* Get tomoyo_realpath of symbolic link. */ -	symlink_program_name = tomoyo_realpath_nofollow(original_name); -	if (!symlink_program_name) +	rn.name = tomoyo_realpath_nofollow(original_name); +	if (!rn.name)  		goto out; +	tomoyo_fill_path_info(&rn); +	need_kfree = true; -	r.name = real_program_name; -	tomoyo_fill_path_info(&r); -	s.name = symlink_program_name; -	tomoyo_fill_path_info(&s); -	l.name = tomoyo_get_last_name(old_domain); -	tomoyo_fill_path_info(&l); - -	/* Check 'alias' directive. */ -	if (tomoyo_pathcmp(&r, &s)) { -		struct tomoyo_alias_entry *ptr; -		/* Is this program allowed to be called via symbolic links? */ -		list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { -			if (ptr->is_deleted || -			    tomoyo_pathcmp(&r, ptr->original_name) || -			    tomoyo_pathcmp(&s, ptr->aliased_name)) +	/* Check 'aggregator' directive. */ +	{ +		struct tomoyo_aggregator *ptr; +		list_for_each_entry_rcu(ptr, &tomoyo_policy_list +					[TOMOYO_ID_AGGREGATOR], head.list) { +			if (ptr->head.is_deleted || +			    !tomoyo_path_matches_pattern(&rn, +							 ptr->original_name))  				continue; -			memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN); -			strncpy(real_program_name, ptr->aliased_name->name, -				TOMOYO_MAX_PATHNAME_LEN - 1); -			tomoyo_fill_path_info(&r); +			kfree(rn.name); +			need_kfree = false; +			/* This is OK because it is read only. */ +			rn = *ptr->aggregated_name;  			break;  		}  	}  	/* Check execute permission. */ -	retval = tomoyo_check_exec_perm(old_domain, &r); +	retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); +	if (retval == TOMOYO_RETRY_REQUEST) +		goto retry;  	if (retval < 0)  		goto out; +	/* +	 * To be able to specify domainnames with wildcards, use the +	 * pathname specified in the policy (which may contain +	 * 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; +	} -	new_domain_name = tmp->buffer; -	if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) { +	/* Calculate domain to transit to. */ +	switch (tomoyo_transition_type(old_domain->domainname, &rn)) { +	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:  		/* Transit to the child of tomoyo_kernel_domain domain. */ -		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, -			 TOMOYO_ROOT_NAME " " "%s", real_program_name); -	} else if (old_domain == &tomoyo_kernel_domain && -		   !tomoyo_policy_loaded) { -		/* -		 * Needn't to transit from kernel domain before starting -		 * /sbin/init. But transit from kernel domain if executing -		 * initializers because they might start before /sbin/init. -		 */ -		domain = old_domain; -	} else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) { +		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " +			 "%s", rn.name); +		break; +	case TOMOYO_TRANSITION_CONTROL_KEEP:  		/* Keep current domain. */  		domain = old_domain; -	} else { -		/* Normal domain transition. */ -		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, -			 "%s %s", old_domain_name, real_program_name); +		break; +	default: +		if (old_domain == &tomoyo_kernel_domain && +		    !tomoyo_policy_loaded) { +			/* +			 * Needn't to transit from kernel domain before +			 * starting /sbin/init. But transit from kernel domain +			 * if executing initializers because they might start +			 * before /sbin/init. +			 */ +			domain = old_domain; +		} else { +			/* Normal domain transition. */ +			snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", +				 old_domain->domainname->name, rn.name); +		} +		break;  	} -	if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN) +	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)  		goto done; -	domain = tomoyo_find_domain(new_domain_name); +	domain = tomoyo_find_domain(tmp);  	if (domain)  		goto done; -	if (is_enforce) -		goto done; -	domain = tomoyo_find_or_assign_new_domain(new_domain_name, -						  old_domain->profile); +	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; +	} +	domain = tomoyo_assign_domain(tmp, old_domain->profile);   done:  	if (domain)  		goto out; -	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", -	       new_domain_name); +	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);  	if (is_enforce)  		retval = -EPERM;  	else @@ -798,8 +535,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)  	/* Update reference count on "struct tomoyo_domain_info". */  	atomic_inc(&domain->users);  	bprm->cred->security = domain; -	kfree(real_program_name); -	kfree(symlink_program_name); +	if (need_kfree) +		kfree(rn.name);  	kfree(tmp);  	return retval;  }  |