162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * security/tomoyo/domain.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2005-2011  NTT DATA CORPORATION
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "common.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/binfmts.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/rculist.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* Variables definitions.*/
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* The initial domain. */
1762306a36Sopenharmony_cistruct tomoyo_domain_info tomoyo_kernel_domain;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/**
2062306a36Sopenharmony_ci * tomoyo_update_policy - Update an entry for exception policy.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * @new_entry:       Pointer to "struct tomoyo_acl_info".
2362306a36Sopenharmony_ci * @size:            Size of @new_entry in bytes.
2462306a36Sopenharmony_ci * @param:           Pointer to "struct tomoyo_acl_param".
2562306a36Sopenharmony_ci * @check_duplicate: Callback function to find duplicated entry.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ciint tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
3262306a36Sopenharmony_ci			 struct tomoyo_acl_param *param,
3362306a36Sopenharmony_ci			 bool (*check_duplicate)(const struct tomoyo_acl_head
3462306a36Sopenharmony_ci						 *,
3562306a36Sopenharmony_ci						 const struct tomoyo_acl_head
3662306a36Sopenharmony_ci						 *))
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	int error = param->is_delete ? -ENOENT : -ENOMEM;
3962306a36Sopenharmony_ci	struct tomoyo_acl_head *entry;
4062306a36Sopenharmony_ci	struct list_head *list = param->list;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (mutex_lock_interruptible(&tomoyo_policy_lock))
4362306a36Sopenharmony_ci		return -ENOMEM;
4462306a36Sopenharmony_ci	list_for_each_entry_rcu(entry, list, list,
4562306a36Sopenharmony_ci				srcu_read_lock_held(&tomoyo_ss)) {
4662306a36Sopenharmony_ci		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
4762306a36Sopenharmony_ci			continue;
4862306a36Sopenharmony_ci		if (!check_duplicate(entry, new_entry))
4962306a36Sopenharmony_ci			continue;
5062306a36Sopenharmony_ci		entry->is_deleted = param->is_delete;
5162306a36Sopenharmony_ci		error = 0;
5262306a36Sopenharmony_ci		break;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	if (error && !param->is_delete) {
5562306a36Sopenharmony_ci		entry = tomoyo_commit_ok(new_entry, size);
5662306a36Sopenharmony_ci		if (entry) {
5762306a36Sopenharmony_ci			list_add_tail_rcu(&entry->list, list);
5862306a36Sopenharmony_ci			error = 0;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	mutex_unlock(&tomoyo_policy_lock);
6262306a36Sopenharmony_ci	return error;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry.
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * @a: Pointer to "struct tomoyo_acl_info".
6962306a36Sopenharmony_ci * @b: Pointer to "struct tomoyo_acl_info".
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * Returns true if @a == @b, false otherwise.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
7462306a36Sopenharmony_ci					const struct tomoyo_acl_info *b)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return a->type == b->type && a->cond == b->cond;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * tomoyo_update_domain - Update an entry for domain policy.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * @new_entry:       Pointer to "struct tomoyo_acl_info".
8362306a36Sopenharmony_ci * @size:            Size of @new_entry in bytes.
8462306a36Sopenharmony_ci * @param:           Pointer to "struct tomoyo_acl_param".
8562306a36Sopenharmony_ci * @check_duplicate: Callback function to find duplicated entry.
8662306a36Sopenharmony_ci * @merge_duplicate: Callback function to merge duplicated entry.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciint tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
9362306a36Sopenharmony_ci			 struct tomoyo_acl_param *param,
9462306a36Sopenharmony_ci			 bool (*check_duplicate)(const struct tomoyo_acl_info
9562306a36Sopenharmony_ci						 *,
9662306a36Sopenharmony_ci						 const struct tomoyo_acl_info
9762306a36Sopenharmony_ci						 *),
9862306a36Sopenharmony_ci			 bool (*merge_duplicate)(struct tomoyo_acl_info *,
9962306a36Sopenharmony_ci						 struct tomoyo_acl_info *,
10062306a36Sopenharmony_ci						 const bool))
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	const bool is_delete = param->is_delete;
10362306a36Sopenharmony_ci	int error = is_delete ? -ENOENT : -ENOMEM;
10462306a36Sopenharmony_ci	struct tomoyo_acl_info *entry;
10562306a36Sopenharmony_ci	struct list_head * const list = param->list;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (param->data[0]) {
10862306a36Sopenharmony_ci		new_entry->cond = tomoyo_get_condition(param);
10962306a36Sopenharmony_ci		if (!new_entry->cond)
11062306a36Sopenharmony_ci			return -EINVAL;
11162306a36Sopenharmony_ci		/*
11262306a36Sopenharmony_ci		 * Domain transition preference is allowed for only
11362306a36Sopenharmony_ci		 * "file execute" entries.
11462306a36Sopenharmony_ci		 */
11562306a36Sopenharmony_ci		if (new_entry->cond->transit &&
11662306a36Sopenharmony_ci		    !(new_entry->type == TOMOYO_TYPE_PATH_ACL &&
11762306a36Sopenharmony_ci		      container_of(new_entry, struct tomoyo_path_acl, head)
11862306a36Sopenharmony_ci		      ->perm == 1 << TOMOYO_TYPE_EXECUTE))
11962306a36Sopenharmony_ci			goto out;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&tomoyo_policy_lock))
12262306a36Sopenharmony_ci		goto out;
12362306a36Sopenharmony_ci	list_for_each_entry_rcu(entry, list, list,
12462306a36Sopenharmony_ci				srcu_read_lock_held(&tomoyo_ss)) {
12562306a36Sopenharmony_ci		if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
12662306a36Sopenharmony_ci			continue;
12762306a36Sopenharmony_ci		if (!tomoyo_same_acl_head(entry, new_entry) ||
12862306a36Sopenharmony_ci		    !check_duplicate(entry, new_entry))
12962306a36Sopenharmony_ci			continue;
13062306a36Sopenharmony_ci		if (merge_duplicate)
13162306a36Sopenharmony_ci			entry->is_deleted = merge_duplicate(entry, new_entry,
13262306a36Sopenharmony_ci							    is_delete);
13362306a36Sopenharmony_ci		else
13462306a36Sopenharmony_ci			entry->is_deleted = is_delete;
13562306a36Sopenharmony_ci		error = 0;
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	if (error && !is_delete) {
13962306a36Sopenharmony_ci		entry = tomoyo_commit_ok(new_entry, size);
14062306a36Sopenharmony_ci		if (entry) {
14162306a36Sopenharmony_ci			list_add_tail_rcu(&entry->list, list);
14262306a36Sopenharmony_ci			error = 0;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	mutex_unlock(&tomoyo_policy_lock);
14662306a36Sopenharmony_ciout:
14762306a36Sopenharmony_ci	tomoyo_put_condition(new_entry->cond);
14862306a36Sopenharmony_ci	return error;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/**
15262306a36Sopenharmony_ci * tomoyo_check_acl - Do permission check.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * @r:           Pointer to "struct tomoyo_request_info".
15562306a36Sopenharmony_ci * @check_entry: Callback function to check type specific parameters.
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_civoid tomoyo_check_acl(struct tomoyo_request_info *r,
16262306a36Sopenharmony_ci		      bool (*check_entry)(struct tomoyo_request_info *,
16362306a36Sopenharmony_ci					  const struct tomoyo_acl_info *))
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	const struct tomoyo_domain_info *domain = r->domain;
16662306a36Sopenharmony_ci	struct tomoyo_acl_info *ptr;
16762306a36Sopenharmony_ci	const struct list_head *list = &domain->acl_info_list;
16862306a36Sopenharmony_ci	u16 i = 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciretry:
17162306a36Sopenharmony_ci	list_for_each_entry_rcu(ptr, list, list,
17262306a36Sopenharmony_ci				srcu_read_lock_held(&tomoyo_ss)) {
17362306a36Sopenharmony_ci		if (ptr->is_deleted || ptr->type != r->param_type)
17462306a36Sopenharmony_ci			continue;
17562306a36Sopenharmony_ci		if (!check_entry(r, ptr))
17662306a36Sopenharmony_ci			continue;
17762306a36Sopenharmony_ci		if (!tomoyo_condition(r, ptr->cond))
17862306a36Sopenharmony_ci			continue;
17962306a36Sopenharmony_ci		r->matched_acl = ptr;
18062306a36Sopenharmony_ci		r->granted = true;
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci	for (; i < TOMOYO_MAX_ACL_GROUPS; i++) {
18462306a36Sopenharmony_ci		if (!test_bit(i, domain->group))
18562306a36Sopenharmony_ci			continue;
18662306a36Sopenharmony_ci		list = &domain->ns->acl_group[i++];
18762306a36Sopenharmony_ci		goto retry;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	r->granted = false;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/* The list for "struct tomoyo_domain_info". */
19362306a36Sopenharmony_ciLIST_HEAD(tomoyo_domain_list);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/**
19662306a36Sopenharmony_ci * tomoyo_last_word - Get last component of a domainname.
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * @name: Domainname to check.
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * Returns the last word of @domainname.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic const char *tomoyo_last_word(const char *name)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	const char *cp = strrchr(name, ' ');
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (cp)
20762306a36Sopenharmony_ci		return cp + 1;
20862306a36Sopenharmony_ci	return name;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/**
21262306a36Sopenharmony_ci * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" entry.
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * @a: Pointer to "struct tomoyo_acl_head".
21562306a36Sopenharmony_ci * @b: Pointer to "struct tomoyo_acl_head".
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * Returns true if @a == @b, false otherwise.
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
22062306a36Sopenharmony_ci					   const struct tomoyo_acl_head *b)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	const struct tomoyo_transition_control *p1 = container_of(a,
22362306a36Sopenharmony_ci								  typeof(*p1),
22462306a36Sopenharmony_ci								  head);
22562306a36Sopenharmony_ci	const struct tomoyo_transition_control *p2 = container_of(b,
22662306a36Sopenharmony_ci								  typeof(*p2),
22762306a36Sopenharmony_ci								  head);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return p1->type == p2->type && p1->is_last_name == p2->is_last_name
23062306a36Sopenharmony_ci		&& p1->domainname == p2->domainname
23162306a36Sopenharmony_ci		&& p1->program == p2->program;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * @param: Pointer to "struct tomoyo_acl_param".
23862306a36Sopenharmony_ci * @type:  Type of this entry.
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_ciint tomoyo_write_transition_control(struct tomoyo_acl_param *param,
24362306a36Sopenharmony_ci				    const u8 type)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct tomoyo_transition_control e = { .type = type };
24662306a36Sopenharmony_ci	int error = param->is_delete ? -ENOENT : -ENOMEM;
24762306a36Sopenharmony_ci	char *program = param->data;
24862306a36Sopenharmony_ci	char *domainname = strstr(program, " from ");
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (domainname) {
25162306a36Sopenharmony_ci		*domainname = '\0';
25262306a36Sopenharmony_ci		domainname += 6;
25362306a36Sopenharmony_ci	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
25462306a36Sopenharmony_ci		   type == TOMOYO_TRANSITION_CONTROL_KEEP) {
25562306a36Sopenharmony_ci		domainname = program;
25662306a36Sopenharmony_ci		program = NULL;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	if (program && strcmp(program, "any")) {
25962306a36Sopenharmony_ci		if (!tomoyo_correct_path(program))
26062306a36Sopenharmony_ci			return -EINVAL;
26162306a36Sopenharmony_ci		e.program = tomoyo_get_name(program);
26262306a36Sopenharmony_ci		if (!e.program)
26362306a36Sopenharmony_ci			goto out;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	if (domainname && strcmp(domainname, "any")) {
26662306a36Sopenharmony_ci		if (!tomoyo_correct_domain(domainname)) {
26762306a36Sopenharmony_ci			if (!tomoyo_correct_path(domainname))
26862306a36Sopenharmony_ci				goto out;
26962306a36Sopenharmony_ci			e.is_last_name = true;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci		e.domainname = tomoyo_get_name(domainname);
27262306a36Sopenharmony_ci		if (!e.domainname)
27362306a36Sopenharmony_ci			goto out;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	param->list = &param->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
27662306a36Sopenharmony_ci	error = tomoyo_update_policy(&e.head, sizeof(e), param,
27762306a36Sopenharmony_ci				     tomoyo_same_transition_control);
27862306a36Sopenharmony_ciout:
27962306a36Sopenharmony_ci	tomoyo_put_name(e.domainname);
28062306a36Sopenharmony_ci	tomoyo_put_name(e.program);
28162306a36Sopenharmony_ci	return error;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci/**
28562306a36Sopenharmony_ci * tomoyo_scan_transition - Try to find specific domain transition type.
28662306a36Sopenharmony_ci *
28762306a36Sopenharmony_ci * @list:       Pointer to "struct list_head".
28862306a36Sopenharmony_ci * @domainname: The name of current domain.
28962306a36Sopenharmony_ci * @program:    The name of requested program.
29062306a36Sopenharmony_ci * @last_name:  The last component of @domainname.
29162306a36Sopenharmony_ci * @type:       One of values in "enum tomoyo_transition_type".
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci * Returns true if found one, false otherwise.
29462306a36Sopenharmony_ci *
29562306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_cistatic inline bool tomoyo_scan_transition
29862306a36Sopenharmony_ci(const struct list_head *list, const struct tomoyo_path_info *domainname,
29962306a36Sopenharmony_ci const struct tomoyo_path_info *program, const char *last_name,
30062306a36Sopenharmony_ci const enum tomoyo_transition_type type)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	const struct tomoyo_transition_control *ptr;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	list_for_each_entry_rcu(ptr, list, head.list,
30562306a36Sopenharmony_ci				srcu_read_lock_held(&tomoyo_ss)) {
30662306a36Sopenharmony_ci		if (ptr->head.is_deleted || ptr->type != type)
30762306a36Sopenharmony_ci			continue;
30862306a36Sopenharmony_ci		if (ptr->domainname) {
30962306a36Sopenharmony_ci			if (!ptr->is_last_name) {
31062306a36Sopenharmony_ci				if (ptr->domainname != domainname)
31162306a36Sopenharmony_ci					continue;
31262306a36Sopenharmony_ci			} else {
31362306a36Sopenharmony_ci				/*
31462306a36Sopenharmony_ci				 * Use direct strcmp() since this is
31562306a36Sopenharmony_ci				 * unlikely used.
31662306a36Sopenharmony_ci				 */
31762306a36Sopenharmony_ci				if (strcmp(ptr->domainname->name, last_name))
31862306a36Sopenharmony_ci					continue;
31962306a36Sopenharmony_ci			}
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci		if (ptr->program && tomoyo_pathcmp(ptr->program, program))
32262306a36Sopenharmony_ci			continue;
32362306a36Sopenharmony_ci		return true;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	return false;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/**
32962306a36Sopenharmony_ci * tomoyo_transition_type - Get domain transition type.
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci * @ns:         Pointer to "struct tomoyo_policy_namespace".
33262306a36Sopenharmony_ci * @domainname: The name of current domain.
33362306a36Sopenharmony_ci * @program:    The name of requested program.
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes
33662306a36Sopenharmony_ci * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if
33762306a36Sopenharmony_ci * executing @program reinitializes domain transition within that namespace,
33862306a36Sopenharmony_ci * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname ,
33962306a36Sopenharmony_ci * others otherwise.
34062306a36Sopenharmony_ci *
34162306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic enum tomoyo_transition_type tomoyo_transition_type
34462306a36Sopenharmony_ci(const struct tomoyo_policy_namespace *ns,
34562306a36Sopenharmony_ci const struct tomoyo_path_info *domainname,
34662306a36Sopenharmony_ci const struct tomoyo_path_info *program)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	const char *last_name = tomoyo_last_word(domainname->name);
34962306a36Sopenharmony_ci	enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	while (type < TOMOYO_MAX_TRANSITION_TYPE) {
35262306a36Sopenharmony_ci		const struct list_head * const list =
35362306a36Sopenharmony_ci			&ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		if (!tomoyo_scan_transition(list, domainname, program,
35662306a36Sopenharmony_ci					    last_name, type)) {
35762306a36Sopenharmony_ci			type++;
35862306a36Sopenharmony_ci			continue;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci		if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET &&
36162306a36Sopenharmony_ci		    type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE)
36262306a36Sopenharmony_ci			break;
36362306a36Sopenharmony_ci		/*
36462306a36Sopenharmony_ci		 * Do not check for reset_domain if no_reset_domain matched.
36562306a36Sopenharmony_ci		 * Do not check for initialize_domain if no_initialize_domain
36662306a36Sopenharmony_ci		 * matched.
36762306a36Sopenharmony_ci		 */
36862306a36Sopenharmony_ci		type++;
36962306a36Sopenharmony_ci		type++;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci	return type;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/**
37562306a36Sopenharmony_ci * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry.
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * @a: Pointer to "struct tomoyo_acl_head".
37862306a36Sopenharmony_ci * @b: Pointer to "struct tomoyo_acl_head".
37962306a36Sopenharmony_ci *
38062306a36Sopenharmony_ci * Returns true if @a == @b, false otherwise.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_cistatic bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
38362306a36Sopenharmony_ci				   const struct tomoyo_acl_head *b)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
38662306a36Sopenharmony_ci							  head);
38762306a36Sopenharmony_ci	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
38862306a36Sopenharmony_ci							  head);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return p1->original_name == p2->original_name &&
39162306a36Sopenharmony_ci		p1->aggregated_name == p2->aggregated_name;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/**
39562306a36Sopenharmony_ci * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci * @param: Pointer to "struct tomoyo_acl_param".
39862306a36Sopenharmony_ci *
39962306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_ciint tomoyo_write_aggregator(struct tomoyo_acl_param *param)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct tomoyo_aggregator e = { };
40662306a36Sopenharmony_ci	int error = param->is_delete ? -ENOENT : -ENOMEM;
40762306a36Sopenharmony_ci	const char *original_name = tomoyo_read_token(param);
40862306a36Sopenharmony_ci	const char *aggregated_name = tomoyo_read_token(param);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (!tomoyo_correct_word(original_name) ||
41162306a36Sopenharmony_ci	    !tomoyo_correct_path(aggregated_name))
41262306a36Sopenharmony_ci		return -EINVAL;
41362306a36Sopenharmony_ci	e.original_name = tomoyo_get_name(original_name);
41462306a36Sopenharmony_ci	e.aggregated_name = tomoyo_get_name(aggregated_name);
41562306a36Sopenharmony_ci	if (!e.original_name || !e.aggregated_name ||
41662306a36Sopenharmony_ci	    e.aggregated_name->is_patterned) /* No patterns allowed. */
41762306a36Sopenharmony_ci		goto out;
41862306a36Sopenharmony_ci	param->list = &param->ns->policy_list[TOMOYO_ID_AGGREGATOR];
41962306a36Sopenharmony_ci	error = tomoyo_update_policy(&e.head, sizeof(e), param,
42062306a36Sopenharmony_ci				     tomoyo_same_aggregator);
42162306a36Sopenharmony_ciout:
42262306a36Sopenharmony_ci	tomoyo_put_name(e.original_name);
42362306a36Sopenharmony_ci	tomoyo_put_name(e.aggregated_name);
42462306a36Sopenharmony_ci	return error;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/**
42862306a36Sopenharmony_ci * tomoyo_find_namespace - Find specified namespace.
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci * @name: Name of namespace to find.
43162306a36Sopenharmony_ci * @len:  Length of @name.
43262306a36Sopenharmony_ci *
43362306a36Sopenharmony_ci * Returns pointer to "struct tomoyo_policy_namespace" if found,
43462306a36Sopenharmony_ci * NULL otherwise.
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
43762306a36Sopenharmony_ci */
43862306a36Sopenharmony_cistatic struct tomoyo_policy_namespace *tomoyo_find_namespace
43962306a36Sopenharmony_ci(const char *name, const unsigned int len)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct tomoyo_policy_namespace *ns;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
44462306a36Sopenharmony_ci		if (strncmp(name, ns->name, len) ||
44562306a36Sopenharmony_ci		    (name[len] && name[len] != ' '))
44662306a36Sopenharmony_ci			continue;
44762306a36Sopenharmony_ci		return ns;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	return NULL;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci/**
45362306a36Sopenharmony_ci * tomoyo_assign_namespace - Create a new namespace.
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * @domainname: Name of namespace to create.
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * Returns pointer to "struct tomoyo_policy_namespace" on success,
45862306a36Sopenharmony_ci * NULL otherwise.
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_cistruct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct tomoyo_policy_namespace *ptr;
46562306a36Sopenharmony_ci	struct tomoyo_policy_namespace *entry;
46662306a36Sopenharmony_ci	const char *cp = domainname;
46762306a36Sopenharmony_ci	unsigned int len = 0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	while (*cp && *cp++ != ' ')
47062306a36Sopenharmony_ci		len++;
47162306a36Sopenharmony_ci	ptr = tomoyo_find_namespace(domainname, len);
47262306a36Sopenharmony_ci	if (ptr)
47362306a36Sopenharmony_ci		return ptr;
47462306a36Sopenharmony_ci	if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname))
47562306a36Sopenharmony_ci		return NULL;
47662306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS | __GFP_NOWARN);
47762306a36Sopenharmony_ci	if (mutex_lock_interruptible(&tomoyo_policy_lock))
47862306a36Sopenharmony_ci		goto out;
47962306a36Sopenharmony_ci	ptr = tomoyo_find_namespace(domainname, len);
48062306a36Sopenharmony_ci	if (!ptr && tomoyo_memory_ok(entry)) {
48162306a36Sopenharmony_ci		char *name = (char *) (entry + 1);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		ptr = entry;
48462306a36Sopenharmony_ci		memmove(name, domainname, len);
48562306a36Sopenharmony_ci		name[len] = '\0';
48662306a36Sopenharmony_ci		entry->name = name;
48762306a36Sopenharmony_ci		tomoyo_init_policy_namespace(entry);
48862306a36Sopenharmony_ci		entry = NULL;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	mutex_unlock(&tomoyo_policy_lock);
49162306a36Sopenharmony_ciout:
49262306a36Sopenharmony_ci	kfree(entry);
49362306a36Sopenharmony_ci	return ptr;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/**
49762306a36Sopenharmony_ci * tomoyo_namespace_jump - Check for namespace jump.
49862306a36Sopenharmony_ci *
49962306a36Sopenharmony_ci * @domainname: Name of domain.
50062306a36Sopenharmony_ci *
50162306a36Sopenharmony_ci * Returns true if namespace differs, false otherwise.
50262306a36Sopenharmony_ci */
50362306a36Sopenharmony_cistatic bool tomoyo_namespace_jump(const char *domainname)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	const char *namespace = tomoyo_current_namespace()->name;
50662306a36Sopenharmony_ci	const int len = strlen(namespace);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return strncmp(domainname, namespace, len) ||
50962306a36Sopenharmony_ci		(domainname[len] && domainname[len] != ' ');
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/**
51362306a36Sopenharmony_ci * tomoyo_assign_domain - Create a domain or a namespace.
51462306a36Sopenharmony_ci *
51562306a36Sopenharmony_ci * @domainname: The name of domain.
51662306a36Sopenharmony_ci * @transit:    True if transit to domain found or created.
51762306a36Sopenharmony_ci *
51862306a36Sopenharmony_ci * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
52162306a36Sopenharmony_ci */
52262306a36Sopenharmony_cistruct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
52362306a36Sopenharmony_ci						const bool transit)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct tomoyo_domain_info e = { };
52662306a36Sopenharmony_ci	struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
52762306a36Sopenharmony_ci	bool created = false;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (entry) {
53062306a36Sopenharmony_ci		if (transit) {
53162306a36Sopenharmony_ci			/*
53262306a36Sopenharmony_ci			 * Since namespace is created at runtime, profiles may
53362306a36Sopenharmony_ci			 * not be created by the moment the process transits to
53462306a36Sopenharmony_ci			 * that domain. Do not perform domain transition if
53562306a36Sopenharmony_ci			 * profile for that domain is not yet created.
53662306a36Sopenharmony_ci			 */
53762306a36Sopenharmony_ci			if (tomoyo_policy_loaded &&
53862306a36Sopenharmony_ci			    !entry->ns->profile_ptr[entry->profile])
53962306a36Sopenharmony_ci				return NULL;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci		return entry;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci	/* Requested domain does not exist. */
54462306a36Sopenharmony_ci	/* Don't create requested domain if domainname is invalid. */
54562306a36Sopenharmony_ci	if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 ||
54662306a36Sopenharmony_ci	    !tomoyo_correct_domain(domainname))
54762306a36Sopenharmony_ci		return NULL;
54862306a36Sopenharmony_ci	/*
54962306a36Sopenharmony_ci	 * Since definition of profiles and acl_groups may differ across
55062306a36Sopenharmony_ci	 * namespaces, do not inherit "use_profile" and "use_group" settings
55162306a36Sopenharmony_ci	 * by automatically creating requested domain upon domain transition.
55262306a36Sopenharmony_ci	 */
55362306a36Sopenharmony_ci	if (transit && tomoyo_namespace_jump(domainname))
55462306a36Sopenharmony_ci		return NULL;
55562306a36Sopenharmony_ci	e.ns = tomoyo_assign_namespace(domainname);
55662306a36Sopenharmony_ci	if (!e.ns)
55762306a36Sopenharmony_ci		return NULL;
55862306a36Sopenharmony_ci	/*
55962306a36Sopenharmony_ci	 * "use_profile" and "use_group" settings for automatically created
56062306a36Sopenharmony_ci	 * domains are inherited from current domain. These are 0 for manually
56162306a36Sopenharmony_ci	 * created domains.
56262306a36Sopenharmony_ci	 */
56362306a36Sopenharmony_ci	if (transit) {
56462306a36Sopenharmony_ci		const struct tomoyo_domain_info *domain = tomoyo_domain();
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		e.profile = domain->profile;
56762306a36Sopenharmony_ci		memcpy(e.group, domain->group, sizeof(e.group));
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci	e.domainname = tomoyo_get_name(domainname);
57062306a36Sopenharmony_ci	if (!e.domainname)
57162306a36Sopenharmony_ci		return NULL;
57262306a36Sopenharmony_ci	if (mutex_lock_interruptible(&tomoyo_policy_lock))
57362306a36Sopenharmony_ci		goto out;
57462306a36Sopenharmony_ci	entry = tomoyo_find_domain(domainname);
57562306a36Sopenharmony_ci	if (!entry) {
57662306a36Sopenharmony_ci		entry = tomoyo_commit_ok(&e, sizeof(e));
57762306a36Sopenharmony_ci		if (entry) {
57862306a36Sopenharmony_ci			INIT_LIST_HEAD(&entry->acl_info_list);
57962306a36Sopenharmony_ci			list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
58062306a36Sopenharmony_ci			created = true;
58162306a36Sopenharmony_ci		}
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	mutex_unlock(&tomoyo_policy_lock);
58462306a36Sopenharmony_ciout:
58562306a36Sopenharmony_ci	tomoyo_put_name(e.domainname);
58662306a36Sopenharmony_ci	if (entry && transit) {
58762306a36Sopenharmony_ci		if (created) {
58862306a36Sopenharmony_ci			struct tomoyo_request_info r;
58962306a36Sopenharmony_ci			int i;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci			tomoyo_init_request_info(&r, entry,
59262306a36Sopenharmony_ci						 TOMOYO_MAC_FILE_EXECUTE);
59362306a36Sopenharmony_ci			r.granted = false;
59462306a36Sopenharmony_ci			tomoyo_write_log(&r, "use_profile %u\n",
59562306a36Sopenharmony_ci					 entry->profile);
59662306a36Sopenharmony_ci			for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
59762306a36Sopenharmony_ci				if (test_bit(i, entry->group))
59862306a36Sopenharmony_ci					tomoyo_write_log(&r, "use_group %u\n",
59962306a36Sopenharmony_ci							 i);
60062306a36Sopenharmony_ci			tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	return entry;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci/**
60762306a36Sopenharmony_ci * tomoyo_environ - Check permission for environment variable names.
60862306a36Sopenharmony_ci *
60962306a36Sopenharmony_ci * @ee: Pointer to "struct tomoyo_execve".
61062306a36Sopenharmony_ci *
61162306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
61262306a36Sopenharmony_ci */
61362306a36Sopenharmony_cistatic int tomoyo_environ(struct tomoyo_execve *ee)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct tomoyo_request_info *r = &ee->r;
61662306a36Sopenharmony_ci	struct linux_binprm *bprm = ee->bprm;
61762306a36Sopenharmony_ci	/* env_page.data is allocated by tomoyo_dump_page(). */
61862306a36Sopenharmony_ci	struct tomoyo_page_dump env_page = { };
61962306a36Sopenharmony_ci	char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
62062306a36Sopenharmony_ci	int arg_len = 0;
62162306a36Sopenharmony_ci	unsigned long pos = bprm->p;
62262306a36Sopenharmony_ci	int offset = pos % PAGE_SIZE;
62362306a36Sopenharmony_ci	int argv_count = bprm->argc;
62462306a36Sopenharmony_ci	int envp_count = bprm->envc;
62562306a36Sopenharmony_ci	int error = -ENOMEM;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	ee->r.type = TOMOYO_MAC_ENVIRON;
62862306a36Sopenharmony_ci	ee->r.profile = r->domain->profile;
62962306a36Sopenharmony_ci	ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile,
63062306a36Sopenharmony_ci				     TOMOYO_MAC_ENVIRON);
63162306a36Sopenharmony_ci	if (!r->mode || !envp_count)
63262306a36Sopenharmony_ci		return 0;
63362306a36Sopenharmony_ci	arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
63462306a36Sopenharmony_ci	if (!arg_ptr)
63562306a36Sopenharmony_ci		goto out;
63662306a36Sopenharmony_ci	while (error == -ENOMEM) {
63762306a36Sopenharmony_ci		if (!tomoyo_dump_page(bprm, pos, &env_page))
63862306a36Sopenharmony_ci			goto out;
63962306a36Sopenharmony_ci		pos += PAGE_SIZE - offset;
64062306a36Sopenharmony_ci		/* Read. */
64162306a36Sopenharmony_ci		while (argv_count && offset < PAGE_SIZE) {
64262306a36Sopenharmony_ci			if (!env_page.data[offset++])
64362306a36Sopenharmony_ci				argv_count--;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci		if (argv_count) {
64662306a36Sopenharmony_ci			offset = 0;
64762306a36Sopenharmony_ci			continue;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci		while (offset < PAGE_SIZE) {
65062306a36Sopenharmony_ci			const unsigned char c = env_page.data[offset++];
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci			if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
65362306a36Sopenharmony_ci				if (c == '=') {
65462306a36Sopenharmony_ci					arg_ptr[arg_len++] = '\0';
65562306a36Sopenharmony_ci				} else if (c == '\\') {
65662306a36Sopenharmony_ci					arg_ptr[arg_len++] = '\\';
65762306a36Sopenharmony_ci					arg_ptr[arg_len++] = '\\';
65862306a36Sopenharmony_ci				} else if (c > ' ' && c < 127) {
65962306a36Sopenharmony_ci					arg_ptr[arg_len++] = c;
66062306a36Sopenharmony_ci				} else {
66162306a36Sopenharmony_ci					arg_ptr[arg_len++] = '\\';
66262306a36Sopenharmony_ci					arg_ptr[arg_len++] = (c >> 6) + '0';
66362306a36Sopenharmony_ci					arg_ptr[arg_len++]
66462306a36Sopenharmony_ci						= ((c >> 3) & 7) + '0';
66562306a36Sopenharmony_ci					arg_ptr[arg_len++] = (c & 7) + '0';
66662306a36Sopenharmony_ci				}
66762306a36Sopenharmony_ci			} else {
66862306a36Sopenharmony_ci				arg_ptr[arg_len] = '\0';
66962306a36Sopenharmony_ci			}
67062306a36Sopenharmony_ci			if (c)
67162306a36Sopenharmony_ci				continue;
67262306a36Sopenharmony_ci			if (tomoyo_env_perm(r, arg_ptr)) {
67362306a36Sopenharmony_ci				error = -EPERM;
67462306a36Sopenharmony_ci				break;
67562306a36Sopenharmony_ci			}
67662306a36Sopenharmony_ci			if (!--envp_count) {
67762306a36Sopenharmony_ci				error = 0;
67862306a36Sopenharmony_ci				break;
67962306a36Sopenharmony_ci			}
68062306a36Sopenharmony_ci			arg_len = 0;
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci		offset = 0;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ciout:
68562306a36Sopenharmony_ci	if (r->mode != TOMOYO_CONFIG_ENFORCING)
68662306a36Sopenharmony_ci		error = 0;
68762306a36Sopenharmony_ci	kfree(env_page.data);
68862306a36Sopenharmony_ci	kfree(arg_ptr);
68962306a36Sopenharmony_ci	return error;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci/**
69362306a36Sopenharmony_ci * tomoyo_find_next_domain - Find a domain.
69462306a36Sopenharmony_ci *
69562306a36Sopenharmony_ci * @bprm: Pointer to "struct linux_binprm".
69662306a36Sopenharmony_ci *
69762306a36Sopenharmony_ci * Returns 0 on success, negative value otherwise.
69862306a36Sopenharmony_ci *
69962306a36Sopenharmony_ci * Caller holds tomoyo_read_lock().
70062306a36Sopenharmony_ci */
70162306a36Sopenharmony_ciint tomoyo_find_next_domain(struct linux_binprm *bprm)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct tomoyo_domain_info *old_domain = tomoyo_domain();
70462306a36Sopenharmony_ci	struct tomoyo_domain_info *domain = NULL;
70562306a36Sopenharmony_ci	const char *original_name = bprm->filename;
70662306a36Sopenharmony_ci	int retval = -ENOMEM;
70762306a36Sopenharmony_ci	bool reject_on_transition_failure = false;
70862306a36Sopenharmony_ci	const struct tomoyo_path_info *candidate;
70962306a36Sopenharmony_ci	struct tomoyo_path_info exename;
71062306a36Sopenharmony_ci	struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (!ee)
71362306a36Sopenharmony_ci		return -ENOMEM;
71462306a36Sopenharmony_ci	ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
71562306a36Sopenharmony_ci	if (!ee->tmp) {
71662306a36Sopenharmony_ci		kfree(ee);
71762306a36Sopenharmony_ci		return -ENOMEM;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci	/* ee->dump->data is allocated by tomoyo_dump_page(). */
72062306a36Sopenharmony_ci	tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE);
72162306a36Sopenharmony_ci	ee->r.ee = ee;
72262306a36Sopenharmony_ci	ee->bprm = bprm;
72362306a36Sopenharmony_ci	ee->r.obj = &ee->obj;
72462306a36Sopenharmony_ci	ee->obj.path1 = bprm->file->f_path;
72562306a36Sopenharmony_ci	/* Get symlink's pathname of program. */
72662306a36Sopenharmony_ci	retval = -ENOENT;
72762306a36Sopenharmony_ci	exename.name = tomoyo_realpath_nofollow(original_name);
72862306a36Sopenharmony_ci	if (!exename.name)
72962306a36Sopenharmony_ci		goto out;
73062306a36Sopenharmony_ci	tomoyo_fill_path_info(&exename);
73162306a36Sopenharmony_ciretry:
73262306a36Sopenharmony_ci	/* Check 'aggregator' directive. */
73362306a36Sopenharmony_ci	{
73462306a36Sopenharmony_ci		struct tomoyo_aggregator *ptr;
73562306a36Sopenharmony_ci		struct list_head *list =
73662306a36Sopenharmony_ci			&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		/* Check 'aggregator' directive. */
73962306a36Sopenharmony_ci		candidate = &exename;
74062306a36Sopenharmony_ci		list_for_each_entry_rcu(ptr, list, head.list,
74162306a36Sopenharmony_ci					srcu_read_lock_held(&tomoyo_ss)) {
74262306a36Sopenharmony_ci			if (ptr->head.is_deleted ||
74362306a36Sopenharmony_ci			    !tomoyo_path_matches_pattern(&exename,
74462306a36Sopenharmony_ci							 ptr->original_name))
74562306a36Sopenharmony_ci				continue;
74662306a36Sopenharmony_ci			candidate = ptr->aggregated_name;
74762306a36Sopenharmony_ci			break;
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* Check execute permission. */
75262306a36Sopenharmony_ci	retval = tomoyo_execute_permission(&ee->r, candidate);
75362306a36Sopenharmony_ci	if (retval == TOMOYO_RETRY_REQUEST)
75462306a36Sopenharmony_ci		goto retry;
75562306a36Sopenharmony_ci	if (retval < 0)
75662306a36Sopenharmony_ci		goto out;
75762306a36Sopenharmony_ci	/*
75862306a36Sopenharmony_ci	 * To be able to specify domainnames with wildcards, use the
75962306a36Sopenharmony_ci	 * pathname specified in the policy (which may contain
76062306a36Sopenharmony_ci	 * wildcard) rather than the pathname passed to execve()
76162306a36Sopenharmony_ci	 * (which never contains wildcard).
76262306a36Sopenharmony_ci	 */
76362306a36Sopenharmony_ci	if (ee->r.param.path.matched_path)
76462306a36Sopenharmony_ci		candidate = ee->r.param.path.matched_path;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/*
76762306a36Sopenharmony_ci	 * Check for domain transition preference if "file execute" matched.
76862306a36Sopenharmony_ci	 * If preference is given, make execve() fail if domain transition
76962306a36Sopenharmony_ci	 * has failed, for domain transition preference should be used with
77062306a36Sopenharmony_ci	 * destination domain defined.
77162306a36Sopenharmony_ci	 */
77262306a36Sopenharmony_ci	if (ee->transition) {
77362306a36Sopenharmony_ci		const char *domainname = ee->transition->name;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		reject_on_transition_failure = true;
77662306a36Sopenharmony_ci		if (!strcmp(domainname, "keep"))
77762306a36Sopenharmony_ci			goto force_keep_domain;
77862306a36Sopenharmony_ci		if (!strcmp(domainname, "child"))
77962306a36Sopenharmony_ci			goto force_child_domain;
78062306a36Sopenharmony_ci		if (!strcmp(domainname, "reset"))
78162306a36Sopenharmony_ci			goto force_reset_domain;
78262306a36Sopenharmony_ci		if (!strcmp(domainname, "initialize"))
78362306a36Sopenharmony_ci			goto force_initialize_domain;
78462306a36Sopenharmony_ci		if (!strcmp(domainname, "parent")) {
78562306a36Sopenharmony_ci			char *cp;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci			strscpy(ee->tmp, old_domain->domainname->name, TOMOYO_EXEC_TMPSIZE);
78862306a36Sopenharmony_ci			cp = strrchr(ee->tmp, ' ');
78962306a36Sopenharmony_ci			if (cp)
79062306a36Sopenharmony_ci				*cp = '\0';
79162306a36Sopenharmony_ci		} else if (*domainname == '<')
79262306a36Sopenharmony_ci			strscpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE);
79362306a36Sopenharmony_ci		else
79462306a36Sopenharmony_ci			snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
79562306a36Sopenharmony_ci				 old_domain->domainname->name, domainname);
79662306a36Sopenharmony_ci		goto force_jump_domain;
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci	/*
79962306a36Sopenharmony_ci	 * No domain transition preference specified.
80062306a36Sopenharmony_ci	 * Calculate domain to transit to.
80162306a36Sopenharmony_ci	 */
80262306a36Sopenharmony_ci	switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
80362306a36Sopenharmony_ci				       candidate)) {
80462306a36Sopenharmony_ci	case TOMOYO_TRANSITION_CONTROL_RESET:
80562306a36Sopenharmony_ciforce_reset_domain:
80662306a36Sopenharmony_ci		/* Transit to the root of specified namespace. */
80762306a36Sopenharmony_ci		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
80862306a36Sopenharmony_ci			 candidate->name);
80962306a36Sopenharmony_ci		/*
81062306a36Sopenharmony_ci		 * Make execve() fail if domain transition across namespaces
81162306a36Sopenharmony_ci		 * has failed.
81262306a36Sopenharmony_ci		 */
81362306a36Sopenharmony_ci		reject_on_transition_failure = true;
81462306a36Sopenharmony_ci		break;
81562306a36Sopenharmony_ci	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
81662306a36Sopenharmony_ciforce_initialize_domain:
81762306a36Sopenharmony_ci		/* Transit to the child of current namespace's root. */
81862306a36Sopenharmony_ci		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
81962306a36Sopenharmony_ci			 old_domain->ns->name, candidate->name);
82062306a36Sopenharmony_ci		break;
82162306a36Sopenharmony_ci	case TOMOYO_TRANSITION_CONTROL_KEEP:
82262306a36Sopenharmony_ciforce_keep_domain:
82362306a36Sopenharmony_ci		/* Keep current domain. */
82462306a36Sopenharmony_ci		domain = old_domain;
82562306a36Sopenharmony_ci		break;
82662306a36Sopenharmony_ci	default:
82762306a36Sopenharmony_ci		if (old_domain == &tomoyo_kernel_domain &&
82862306a36Sopenharmony_ci		    !tomoyo_policy_loaded) {
82962306a36Sopenharmony_ci			/*
83062306a36Sopenharmony_ci			 * Needn't to transit from kernel domain before
83162306a36Sopenharmony_ci			 * starting /sbin/init. But transit from kernel domain
83262306a36Sopenharmony_ci			 * if executing initializers because they might start
83362306a36Sopenharmony_ci			 * before /sbin/init.
83462306a36Sopenharmony_ci			 */
83562306a36Sopenharmony_ci			domain = old_domain;
83662306a36Sopenharmony_ci			break;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ciforce_child_domain:
83962306a36Sopenharmony_ci		/* Normal domain transition. */
84062306a36Sopenharmony_ci		snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
84162306a36Sopenharmony_ci			 old_domain->domainname->name, candidate->name);
84262306a36Sopenharmony_ci		break;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ciforce_jump_domain:
84562306a36Sopenharmony_ci	if (!domain)
84662306a36Sopenharmony_ci		domain = tomoyo_assign_domain(ee->tmp, true);
84762306a36Sopenharmony_ci	if (domain)
84862306a36Sopenharmony_ci		retval = 0;
84962306a36Sopenharmony_ci	else if (reject_on_transition_failure) {
85062306a36Sopenharmony_ci		pr_warn("ERROR: Domain '%s' not ready.\n", ee->tmp);
85162306a36Sopenharmony_ci		retval = -ENOMEM;
85262306a36Sopenharmony_ci	} else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
85362306a36Sopenharmony_ci		retval = -ENOMEM;
85462306a36Sopenharmony_ci	else {
85562306a36Sopenharmony_ci		retval = 0;
85662306a36Sopenharmony_ci		if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) {
85762306a36Sopenharmony_ci			old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true;
85862306a36Sopenharmony_ci			ee->r.granted = false;
85962306a36Sopenharmony_ci			tomoyo_write_log(&ee->r, "%s", tomoyo_dif
86062306a36Sopenharmony_ci					 [TOMOYO_DIF_TRANSITION_FAILED]);
86162306a36Sopenharmony_ci			pr_warn("ERROR: Domain '%s' not defined.\n", ee->tmp);
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci out:
86562306a36Sopenharmony_ci	if (!domain)
86662306a36Sopenharmony_ci		domain = old_domain;
86762306a36Sopenharmony_ci	/* Update reference count on "struct tomoyo_domain_info". */
86862306a36Sopenharmony_ci	{
86962306a36Sopenharmony_ci		struct tomoyo_task *s = tomoyo_task(current);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		s->old_domain_info = s->domain_info;
87262306a36Sopenharmony_ci		s->domain_info = domain;
87362306a36Sopenharmony_ci		atomic_inc(&domain->users);
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci	kfree(exename.name);
87662306a36Sopenharmony_ci	if (!retval) {
87762306a36Sopenharmony_ci		ee->r.domain = domain;
87862306a36Sopenharmony_ci		retval = tomoyo_environ(ee);
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci	kfree(ee->tmp);
88162306a36Sopenharmony_ci	kfree(ee->dump.data);
88262306a36Sopenharmony_ci	kfree(ee);
88362306a36Sopenharmony_ci	return retval;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/**
88762306a36Sopenharmony_ci * tomoyo_dump_page - Dump a page to buffer.
88862306a36Sopenharmony_ci *
88962306a36Sopenharmony_ci * @bprm: Pointer to "struct linux_binprm".
89062306a36Sopenharmony_ci * @pos:  Location to dump.
89162306a36Sopenharmony_ci * @dump: Pointer to "struct tomoyo_page_dump".
89262306a36Sopenharmony_ci *
89362306a36Sopenharmony_ci * Returns true on success, false otherwise.
89462306a36Sopenharmony_ci */
89562306a36Sopenharmony_cibool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
89662306a36Sopenharmony_ci		      struct tomoyo_page_dump *dump)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct page *page;
89962306a36Sopenharmony_ci#ifdef CONFIG_MMU
90062306a36Sopenharmony_ci	int ret;
90162306a36Sopenharmony_ci#endif
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* dump->data is released by tomoyo_find_next_domain(). */
90462306a36Sopenharmony_ci	if (!dump->data) {
90562306a36Sopenharmony_ci		dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
90662306a36Sopenharmony_ci		if (!dump->data)
90762306a36Sopenharmony_ci			return false;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	/* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
91062306a36Sopenharmony_ci#ifdef CONFIG_MMU
91162306a36Sopenharmony_ci	/*
91262306a36Sopenharmony_ci	 * This is called at execve() time in order to dig around
91362306a36Sopenharmony_ci	 * in the argv/environment of the new proceess
91462306a36Sopenharmony_ci	 * (represented by bprm).
91562306a36Sopenharmony_ci	 */
91662306a36Sopenharmony_ci	mmap_read_lock(bprm->mm);
91762306a36Sopenharmony_ci	ret = get_user_pages_remote(bprm->mm, pos, 1,
91862306a36Sopenharmony_ci				    FOLL_FORCE, &page, NULL);
91962306a36Sopenharmony_ci	mmap_read_unlock(bprm->mm);
92062306a36Sopenharmony_ci	if (ret <= 0)
92162306a36Sopenharmony_ci		return false;
92262306a36Sopenharmony_ci#else
92362306a36Sopenharmony_ci	page = bprm->page[pos / PAGE_SIZE];
92462306a36Sopenharmony_ci#endif
92562306a36Sopenharmony_ci	if (page != dump->page) {
92662306a36Sopenharmony_ci		const unsigned int offset = pos % PAGE_SIZE;
92762306a36Sopenharmony_ci		/*
92862306a36Sopenharmony_ci		 * Maybe kmap()/kunmap() should be used here.
92962306a36Sopenharmony_ci		 * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
93062306a36Sopenharmony_ci		 * So do I.
93162306a36Sopenharmony_ci		 */
93262306a36Sopenharmony_ci		char *kaddr = kmap_atomic(page);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		dump->page = page;
93562306a36Sopenharmony_ci		memcpy(dump->data + offset, kaddr + offset,
93662306a36Sopenharmony_ci		       PAGE_SIZE - offset);
93762306a36Sopenharmony_ci		kunmap_atomic(kaddr);
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	/* Same with put_arg_page(page) in fs/exec.c */
94062306a36Sopenharmony_ci#ifdef CONFIG_MMU
94162306a36Sopenharmony_ci	put_page(page);
94262306a36Sopenharmony_ci#endif
94362306a36Sopenharmony_ci	return true;
94462306a36Sopenharmony_ci}
945