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 = ¶m->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 = ¶m->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