162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Authors: Karl MacMillan <kmacmillan@tresys.com> 362306a36Sopenharmony_ci * Frank Mayer <mayerf@tresys.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003 - 2004 Tresys Technology, LLC 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "security.h" 1562306a36Sopenharmony_ci#include "conditional.h" 1662306a36Sopenharmony_ci#include "services.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * cond_evaluate_expr evaluates a conditional expr 2062306a36Sopenharmony_ci * in reverse polish notation. It returns true (1), false (0), 2162306a36Sopenharmony_ci * or undefined (-1). Undefined occurs when the expression 2262306a36Sopenharmony_ci * exceeds the stack depth of COND_EXPR_MAXDEPTH. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci u32 i; 2762306a36Sopenharmony_ci int s[COND_EXPR_MAXDEPTH]; 2862306a36Sopenharmony_ci int sp = -1; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (expr->len == 0) 3162306a36Sopenharmony_ci return -1; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci for (i = 0; i < expr->len; i++) { 3462306a36Sopenharmony_ci struct cond_expr_node *node = &expr->nodes[i]; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci switch (node->expr_type) { 3762306a36Sopenharmony_ci case COND_BOOL: 3862306a36Sopenharmony_ci if (sp == (COND_EXPR_MAXDEPTH - 1)) 3962306a36Sopenharmony_ci return -1; 4062306a36Sopenharmony_ci sp++; 4162306a36Sopenharmony_ci s[sp] = p->bool_val_to_struct[node->boolean - 1]->state; 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci case COND_NOT: 4462306a36Sopenharmony_ci if (sp < 0) 4562306a36Sopenharmony_ci return -1; 4662306a36Sopenharmony_ci s[sp] = !s[sp]; 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci case COND_OR: 4962306a36Sopenharmony_ci if (sp < 1) 5062306a36Sopenharmony_ci return -1; 5162306a36Sopenharmony_ci sp--; 5262306a36Sopenharmony_ci s[sp] |= s[sp + 1]; 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci case COND_AND: 5562306a36Sopenharmony_ci if (sp < 1) 5662306a36Sopenharmony_ci return -1; 5762306a36Sopenharmony_ci sp--; 5862306a36Sopenharmony_ci s[sp] &= s[sp + 1]; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case COND_XOR: 6162306a36Sopenharmony_ci if (sp < 1) 6262306a36Sopenharmony_ci return -1; 6362306a36Sopenharmony_ci sp--; 6462306a36Sopenharmony_ci s[sp] ^= s[sp + 1]; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case COND_EQ: 6762306a36Sopenharmony_ci if (sp < 1) 6862306a36Sopenharmony_ci return -1; 6962306a36Sopenharmony_ci sp--; 7062306a36Sopenharmony_ci s[sp] = (s[sp] == s[sp + 1]); 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case COND_NEQ: 7362306a36Sopenharmony_ci if (sp < 1) 7462306a36Sopenharmony_ci return -1; 7562306a36Sopenharmony_ci sp--; 7662306a36Sopenharmony_ci s[sp] = (s[sp] != s[sp + 1]); 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci default: 7962306a36Sopenharmony_ci return -1; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci return s[0]; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * evaluate_cond_node evaluates the conditional stored in 8762306a36Sopenharmony_ci * a struct cond_node and if the result is different than the 8862306a36Sopenharmony_ci * current state of the node it sets the rules in the true/false 8962306a36Sopenharmony_ci * list appropriately. If the result of the expression is undefined 9062306a36Sopenharmony_ci * all of the rules are disabled for safety. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic void evaluate_cond_node(struct policydb *p, struct cond_node *node) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct avtab_node *avnode; 9562306a36Sopenharmony_ci int new_state; 9662306a36Sopenharmony_ci u32 i; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci new_state = cond_evaluate_expr(p, &node->expr); 9962306a36Sopenharmony_ci if (new_state != node->cur_state) { 10062306a36Sopenharmony_ci node->cur_state = new_state; 10162306a36Sopenharmony_ci if (new_state == -1) 10262306a36Sopenharmony_ci pr_err("SELinux: expression result was undefined - disabling all rules.\n"); 10362306a36Sopenharmony_ci /* turn the rules on or off */ 10462306a36Sopenharmony_ci for (i = 0; i < node->true_list.len; i++) { 10562306a36Sopenharmony_ci avnode = node->true_list.nodes[i]; 10662306a36Sopenharmony_ci if (new_state <= 0) 10762306a36Sopenharmony_ci avnode->key.specified &= ~AVTAB_ENABLED; 10862306a36Sopenharmony_ci else 10962306a36Sopenharmony_ci avnode->key.specified |= AVTAB_ENABLED; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < node->false_list.len; i++) { 11362306a36Sopenharmony_ci avnode = node->false_list.nodes[i]; 11462306a36Sopenharmony_ci /* -1 or 1 */ 11562306a36Sopenharmony_ci if (new_state) 11662306a36Sopenharmony_ci avnode->key.specified &= ~AVTAB_ENABLED; 11762306a36Sopenharmony_ci else 11862306a36Sopenharmony_ci avnode->key.specified |= AVTAB_ENABLED; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_civoid evaluate_cond_nodes(struct policydb *p) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 i; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci for (i = 0; i < p->cond_list_len; i++) 12862306a36Sopenharmony_ci evaluate_cond_node(p, &p->cond_list[i]); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid cond_policydb_init(struct policydb *p) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci p->bool_val_to_struct = NULL; 13462306a36Sopenharmony_ci p->cond_list = NULL; 13562306a36Sopenharmony_ci p->cond_list_len = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci avtab_init(&p->te_cond_avtab); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void cond_node_destroy(struct cond_node *node) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci kfree(node->expr.nodes); 14362306a36Sopenharmony_ci /* the avtab_ptr_t nodes are destroyed by the avtab */ 14462306a36Sopenharmony_ci kfree(node->true_list.nodes); 14562306a36Sopenharmony_ci kfree(node->false_list.nodes); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void cond_list_destroy(struct policydb *p) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci u32 i; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (i = 0; i < p->cond_list_len; i++) 15362306a36Sopenharmony_ci cond_node_destroy(&p->cond_list[i]); 15462306a36Sopenharmony_ci kfree(p->cond_list); 15562306a36Sopenharmony_ci p->cond_list = NULL; 15662306a36Sopenharmony_ci p->cond_list_len = 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_civoid cond_policydb_destroy(struct policydb *p) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci kfree(p->bool_val_to_struct); 16262306a36Sopenharmony_ci avtab_destroy(&p->te_cond_avtab); 16362306a36Sopenharmony_ci cond_list_destroy(p); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ciint cond_init_bool_indexes(struct policydb *p) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci kfree(p->bool_val_to_struct); 16962306a36Sopenharmony_ci p->bool_val_to_struct = kmalloc_array(p->p_bools.nprim, 17062306a36Sopenharmony_ci sizeof(*p->bool_val_to_struct), 17162306a36Sopenharmony_ci GFP_KERNEL); 17262306a36Sopenharmony_ci if (!p->bool_val_to_struct) 17362306a36Sopenharmony_ci return -ENOMEM; 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint cond_destroy_bool(void *key, void *datum, void *p) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci kfree(key); 18062306a36Sopenharmony_ci kfree(datum); 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciint cond_index_bool(void *key, void *datum, void *datap) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct policydb *p; 18762306a36Sopenharmony_ci struct cond_bool_datum *booldatum; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci booldatum = datum; 19062306a36Sopenharmony_ci p = datap; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!booldatum->value || booldatum->value > p->p_bools.nprim) 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci p->sym_val_to_name[SYM_BOOLS][booldatum->value - 1] = key; 19662306a36Sopenharmony_ci p->bool_val_to_struct[booldatum->value - 1] = booldatum; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int bool_isvalid(struct cond_bool_datum *b) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (!(b->state == 0 || b->state == 1)) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci return 1; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint cond_read_bool(struct policydb *p, struct symtab *s, void *fp) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci char *key = NULL; 21162306a36Sopenharmony_ci struct cond_bool_datum *booldatum; 21262306a36Sopenharmony_ci __le32 buf[3]; 21362306a36Sopenharmony_ci u32 len; 21462306a36Sopenharmony_ci int rc; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL); 21762306a36Sopenharmony_ci if (!booldatum) 21862306a36Sopenharmony_ci return -ENOMEM; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci rc = next_entry(buf, fp, sizeof(buf)); 22162306a36Sopenharmony_ci if (rc) 22262306a36Sopenharmony_ci goto err; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci booldatum->value = le32_to_cpu(buf[0]); 22562306a36Sopenharmony_ci booldatum->state = le32_to_cpu(buf[1]); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci rc = -EINVAL; 22862306a36Sopenharmony_ci if (!bool_isvalid(booldatum)) 22962306a36Sopenharmony_ci goto err; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci len = le32_to_cpu(buf[2]); 23262306a36Sopenharmony_ci if (((len == 0) || (len == (u32)-1))) 23362306a36Sopenharmony_ci goto err; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci rc = -ENOMEM; 23662306a36Sopenharmony_ci key = kmalloc(len + 1, GFP_KERNEL); 23762306a36Sopenharmony_ci if (!key) 23862306a36Sopenharmony_ci goto err; 23962306a36Sopenharmony_ci rc = next_entry(key, fp, len); 24062306a36Sopenharmony_ci if (rc) 24162306a36Sopenharmony_ci goto err; 24262306a36Sopenharmony_ci key[len] = '\0'; 24362306a36Sopenharmony_ci rc = symtab_insert(s, key, booldatum); 24462306a36Sopenharmony_ci if (rc) 24562306a36Sopenharmony_ci goto err; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_cierr: 24962306a36Sopenharmony_ci cond_destroy_bool(key, booldatum, NULL); 25062306a36Sopenharmony_ci return rc; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistruct cond_insertf_data { 25462306a36Sopenharmony_ci struct policydb *p; 25562306a36Sopenharmony_ci struct avtab_node **dst; 25662306a36Sopenharmony_ci struct cond_av_list *other; 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int cond_insertf(struct avtab *a, const struct avtab_key *k, 26062306a36Sopenharmony_ci const struct avtab_datum *d, void *ptr) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct cond_insertf_data *data = ptr; 26362306a36Sopenharmony_ci struct policydb *p = data->p; 26462306a36Sopenharmony_ci struct cond_av_list *other = data->other; 26562306a36Sopenharmony_ci struct avtab_node *node_ptr; 26662306a36Sopenharmony_ci u32 i; 26762306a36Sopenharmony_ci bool found; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * For type rules we have to make certain there aren't any 27162306a36Sopenharmony_ci * conflicting rules by searching the te_avtab and the 27262306a36Sopenharmony_ci * cond_te_avtab. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci if (k->specified & AVTAB_TYPE) { 27562306a36Sopenharmony_ci if (avtab_search_node(&p->te_avtab, k)) { 27662306a36Sopenharmony_ci pr_err("SELinux: type rule already exists outside of a conditional.\n"); 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * If we are reading the false list other will be a pointer to 28162306a36Sopenharmony_ci * the true list. We can have duplicate entries if there is only 28262306a36Sopenharmony_ci * 1 other entry and it is in our true list. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * If we are reading the true list (other == NULL) there shouldn't 28562306a36Sopenharmony_ci * be any other entries. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci if (other) { 28862306a36Sopenharmony_ci node_ptr = avtab_search_node(&p->te_cond_avtab, k); 28962306a36Sopenharmony_ci if (node_ptr) { 29062306a36Sopenharmony_ci if (avtab_search_node_next(node_ptr, k->specified)) { 29162306a36Sopenharmony_ci pr_err("SELinux: too many conflicting type rules.\n"); 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci found = false; 29562306a36Sopenharmony_ci for (i = 0; i < other->len; i++) { 29662306a36Sopenharmony_ci if (other->nodes[i] == node_ptr) { 29762306a36Sopenharmony_ci found = true; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci if (!found) { 30262306a36Sopenharmony_ci pr_err("SELinux: conflicting type rules.\n"); 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci } else { 30762306a36Sopenharmony_ci if (avtab_search_node(&p->te_cond_avtab, k)) { 30862306a36Sopenharmony_ci pr_err("SELinux: conflicting type rules when adding type rule for true.\n"); 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); 31562306a36Sopenharmony_ci if (!node_ptr) { 31662306a36Sopenharmony_ci pr_err("SELinux: could not insert rule.\n"); 31762306a36Sopenharmony_ci return -ENOMEM; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci *data->dst = node_ptr; 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int cond_read_av_list(struct policydb *p, void *fp, 32562306a36Sopenharmony_ci struct cond_av_list *list, 32662306a36Sopenharmony_ci struct cond_av_list *other) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int rc; 32962306a36Sopenharmony_ci __le32 buf[1]; 33062306a36Sopenharmony_ci u32 i, len; 33162306a36Sopenharmony_ci struct cond_insertf_data data; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci rc = next_entry(buf, fp, sizeof(u32)); 33462306a36Sopenharmony_ci if (rc) 33562306a36Sopenharmony_ci return rc; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci len = le32_to_cpu(buf[0]); 33862306a36Sopenharmony_ci if (len == 0) 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci list->nodes = kcalloc(len, sizeof(*list->nodes), GFP_KERNEL); 34262306a36Sopenharmony_ci if (!list->nodes) 34362306a36Sopenharmony_ci return -ENOMEM; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci data.p = p; 34662306a36Sopenharmony_ci data.other = other; 34762306a36Sopenharmony_ci for (i = 0; i < len; i++) { 34862306a36Sopenharmony_ci data.dst = &list->nodes[i]; 34962306a36Sopenharmony_ci rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf, 35062306a36Sopenharmony_ci &data); 35162306a36Sopenharmony_ci if (rc) { 35262306a36Sopenharmony_ci kfree(list->nodes); 35362306a36Sopenharmony_ci list->nodes = NULL; 35462306a36Sopenharmony_ci return rc; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci list->len = len; 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { 36562306a36Sopenharmony_ci pr_err("SELinux: conditional expressions uses unknown operator.\n"); 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (expr->boolean > p->p_bools.nprim) { 37062306a36Sopenharmony_ci pr_err("SELinux: conditional expressions uses unknown bool.\n"); 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci return 1; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci __le32 buf[2]; 37962306a36Sopenharmony_ci u32 i, len; 38062306a36Sopenharmony_ci int rc; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci rc = next_entry(buf, fp, sizeof(u32) * 2); 38362306a36Sopenharmony_ci if (rc) 38462306a36Sopenharmony_ci return rc; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci node->cur_state = le32_to_cpu(buf[0]); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* expr */ 38962306a36Sopenharmony_ci len = le32_to_cpu(buf[1]); 39062306a36Sopenharmony_ci node->expr.nodes = kcalloc(len, sizeof(*node->expr.nodes), GFP_KERNEL); 39162306a36Sopenharmony_ci if (!node->expr.nodes) 39262306a36Sopenharmony_ci return -ENOMEM; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci node->expr.len = len; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < len; i++) { 39762306a36Sopenharmony_ci struct cond_expr_node *expr = &node->expr.nodes[i]; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci rc = next_entry(buf, fp, sizeof(u32) * 2); 40062306a36Sopenharmony_ci if (rc) 40162306a36Sopenharmony_ci return rc; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci expr->expr_type = le32_to_cpu(buf[0]); 40462306a36Sopenharmony_ci expr->boolean = le32_to_cpu(buf[1]); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!expr_node_isvalid(p, expr)) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci rc = cond_read_av_list(p, fp, &node->true_list, NULL); 41162306a36Sopenharmony_ci if (rc) 41262306a36Sopenharmony_ci return rc; 41362306a36Sopenharmony_ci return cond_read_av_list(p, fp, &node->false_list, &node->true_list); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciint cond_read_list(struct policydb *p, void *fp) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci __le32 buf[1]; 41962306a36Sopenharmony_ci u32 i, len; 42062306a36Sopenharmony_ci int rc; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci rc = next_entry(buf, fp, sizeof(buf)); 42362306a36Sopenharmony_ci if (rc) 42462306a36Sopenharmony_ci return rc; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci len = le32_to_cpu(buf[0]); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci p->cond_list = kcalloc(len, sizeof(*p->cond_list), GFP_KERNEL); 42962306a36Sopenharmony_ci if (!p->cond_list) 43062306a36Sopenharmony_ci return -ENOMEM; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel); 43362306a36Sopenharmony_ci if (rc) 43462306a36Sopenharmony_ci goto err; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci p->cond_list_len = len; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (i = 0; i < len; i++) { 43962306a36Sopenharmony_ci rc = cond_read_node(p, &p->cond_list[i], fp); 44062306a36Sopenharmony_ci if (rc) 44162306a36Sopenharmony_ci goto err; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_cierr: 44562306a36Sopenharmony_ci cond_list_destroy(p); 44662306a36Sopenharmony_ci return rc; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ciint cond_write_bool(void *vkey, void *datum, void *ptr) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci char *key = vkey; 45262306a36Sopenharmony_ci struct cond_bool_datum *booldatum = datum; 45362306a36Sopenharmony_ci struct policy_data *pd = ptr; 45462306a36Sopenharmony_ci void *fp = pd->fp; 45562306a36Sopenharmony_ci __le32 buf[3]; 45662306a36Sopenharmony_ci u32 len; 45762306a36Sopenharmony_ci int rc; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci len = strlen(key); 46062306a36Sopenharmony_ci buf[0] = cpu_to_le32(booldatum->value); 46162306a36Sopenharmony_ci buf[1] = cpu_to_le32(booldatum->state); 46262306a36Sopenharmony_ci buf[2] = cpu_to_le32(len); 46362306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 3, fp); 46462306a36Sopenharmony_ci if (rc) 46562306a36Sopenharmony_ci return rc; 46662306a36Sopenharmony_ci rc = put_entry(key, 1, len, fp); 46762306a36Sopenharmony_ci if (rc) 46862306a36Sopenharmony_ci return rc; 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* 47362306a36Sopenharmony_ci * cond_write_cond_av_list doesn't write out the av_list nodes. 47462306a36Sopenharmony_ci * Instead it writes out the key/value pairs from the avtab. This 47562306a36Sopenharmony_ci * is necessary because there is no way to uniquely identifying rules 47662306a36Sopenharmony_ci * in the avtab so it is not possible to associate individual rules 47762306a36Sopenharmony_ci * in the avtab with a conditional without saving them as part of 47862306a36Sopenharmony_ci * the conditional. This means that the avtab with the conditional 47962306a36Sopenharmony_ci * rules will not be saved but will be rebuilt on policy load. 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_cistatic int cond_write_av_list(struct policydb *p, 48262306a36Sopenharmony_ci struct cond_av_list *list, struct policy_file *fp) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci __le32 buf[1]; 48562306a36Sopenharmony_ci u32 i; 48662306a36Sopenharmony_ci int rc; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci buf[0] = cpu_to_le32(list->len); 48962306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 1, fp); 49062306a36Sopenharmony_ci if (rc) 49162306a36Sopenharmony_ci return rc; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci for (i = 0; i < list->len; i++) { 49462306a36Sopenharmony_ci rc = avtab_write_item(p, list->nodes[i], fp); 49562306a36Sopenharmony_ci if (rc) 49662306a36Sopenharmony_ci return rc; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int cond_write_node(struct policydb *p, struct cond_node *node, 50362306a36Sopenharmony_ci struct policy_file *fp) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci __le32 buf[2]; 50662306a36Sopenharmony_ci int rc; 50762306a36Sopenharmony_ci u32 i; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci buf[0] = cpu_to_le32(node->cur_state); 51062306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 1, fp); 51162306a36Sopenharmony_ci if (rc) 51262306a36Sopenharmony_ci return rc; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci buf[0] = cpu_to_le32(node->expr.len); 51562306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 1, fp); 51662306a36Sopenharmony_ci if (rc) 51762306a36Sopenharmony_ci return rc; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci for (i = 0; i < node->expr.len; i++) { 52062306a36Sopenharmony_ci buf[0] = cpu_to_le32(node->expr.nodes[i].expr_type); 52162306a36Sopenharmony_ci buf[1] = cpu_to_le32(node->expr.nodes[i].boolean); 52262306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 2, fp); 52362306a36Sopenharmony_ci if (rc) 52462306a36Sopenharmony_ci return rc; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci rc = cond_write_av_list(p, &node->true_list, fp); 52862306a36Sopenharmony_ci if (rc) 52962306a36Sopenharmony_ci return rc; 53062306a36Sopenharmony_ci rc = cond_write_av_list(p, &node->false_list, fp); 53162306a36Sopenharmony_ci if (rc) 53262306a36Sopenharmony_ci return rc; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ciint cond_write_list(struct policydb *p, void *fp) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci u32 i; 54062306a36Sopenharmony_ci __le32 buf[1]; 54162306a36Sopenharmony_ci int rc; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci buf[0] = cpu_to_le32(p->cond_list_len); 54462306a36Sopenharmony_ci rc = put_entry(buf, sizeof(u32), 1, fp); 54562306a36Sopenharmony_ci if (rc) 54662306a36Sopenharmony_ci return rc; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci for (i = 0; i < p->cond_list_len; i++) { 54962306a36Sopenharmony_ci rc = cond_write_node(p, &p->cond_list[i], fp); 55062306a36Sopenharmony_ci if (rc) 55162306a36Sopenharmony_ci return rc; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_civoid cond_compute_xperms(struct avtab *ctab, struct avtab_key *key, 55862306a36Sopenharmony_ci struct extended_perms_decision *xpermd) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct avtab_node *node; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (!ctab || !key || !xpermd) 56362306a36Sopenharmony_ci return; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci for (node = avtab_search_node(ctab, key); node; 56662306a36Sopenharmony_ci node = avtab_search_node_next(node, key->specified)) { 56762306a36Sopenharmony_ci if (node->key.specified & AVTAB_ENABLED) 56862306a36Sopenharmony_ci services_compute_xperms_decision(xpermd, node); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci/* Determine whether additional permissions are granted by the conditional 57262306a36Sopenharmony_ci * av table, and if so, add them to the result 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_civoid cond_compute_av(struct avtab *ctab, struct avtab_key *key, 57562306a36Sopenharmony_ci struct av_decision *avd, struct extended_perms *xperms) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct avtab_node *node; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!ctab || !key || !avd) 58062306a36Sopenharmony_ci return; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci for (node = avtab_search_node(ctab, key); node; 58362306a36Sopenharmony_ci node = avtab_search_node_next(node, key->specified)) { 58462306a36Sopenharmony_ci if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == 58562306a36Sopenharmony_ci (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) 58662306a36Sopenharmony_ci avd->allowed |= node->datum.u.data; 58762306a36Sopenharmony_ci if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == 58862306a36Sopenharmony_ci (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) 58962306a36Sopenharmony_ci /* Since a '0' in an auditdeny mask represents a 59062306a36Sopenharmony_ci * permission we do NOT want to audit (dontaudit), we use 59162306a36Sopenharmony_ci * the '&' operand to ensure that all '0's in the mask 59262306a36Sopenharmony_ci * are retained (much unlike the allow and auditallow cases). 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci avd->auditdeny &= node->datum.u.data; 59562306a36Sopenharmony_ci if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == 59662306a36Sopenharmony_ci (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) 59762306a36Sopenharmony_ci avd->auditallow |= node->datum.u.data; 59862306a36Sopenharmony_ci if (xperms && (node->key.specified & AVTAB_ENABLED) && 59962306a36Sopenharmony_ci (node->key.specified & AVTAB_XPERMS)) 60062306a36Sopenharmony_ci services_compute_xperms_drivers(xperms, node); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int cond_dup_av_list(struct cond_av_list *new, 60562306a36Sopenharmony_ci struct cond_av_list *orig, 60662306a36Sopenharmony_ci struct avtab *avtab) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci u32 i; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci memset(new, 0, sizeof(*new)); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci new->nodes = kcalloc(orig->len, sizeof(*new->nodes), GFP_KERNEL); 61362306a36Sopenharmony_ci if (!new->nodes) 61462306a36Sopenharmony_ci return -ENOMEM; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci for (i = 0; i < orig->len; i++) { 61762306a36Sopenharmony_ci new->nodes[i] = avtab_insert_nonunique(avtab, 61862306a36Sopenharmony_ci &orig->nodes[i]->key, 61962306a36Sopenharmony_ci &orig->nodes[i]->datum); 62062306a36Sopenharmony_ci if (!new->nodes[i]) 62162306a36Sopenharmony_ci return -ENOMEM; 62262306a36Sopenharmony_ci new->len++; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int duplicate_policydb_cond_list(struct policydb *newp, 62962306a36Sopenharmony_ci struct policydb *origp) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci int rc; 63262306a36Sopenharmony_ci u32 i; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci rc = avtab_alloc_dup(&newp->te_cond_avtab, &origp->te_cond_avtab); 63562306a36Sopenharmony_ci if (rc) 63662306a36Sopenharmony_ci return rc; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci newp->cond_list_len = 0; 63962306a36Sopenharmony_ci newp->cond_list = kcalloc(origp->cond_list_len, 64062306a36Sopenharmony_ci sizeof(*newp->cond_list), 64162306a36Sopenharmony_ci GFP_KERNEL); 64262306a36Sopenharmony_ci if (!newp->cond_list) 64362306a36Sopenharmony_ci goto error; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci for (i = 0; i < origp->cond_list_len; i++) { 64662306a36Sopenharmony_ci struct cond_node *newn = &newp->cond_list[i]; 64762306a36Sopenharmony_ci struct cond_node *orign = &origp->cond_list[i]; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci newp->cond_list_len++; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci newn->cur_state = orign->cur_state; 65262306a36Sopenharmony_ci newn->expr.nodes = kmemdup(orign->expr.nodes, 65362306a36Sopenharmony_ci orign->expr.len * sizeof(*orign->expr.nodes), 65462306a36Sopenharmony_ci GFP_KERNEL); 65562306a36Sopenharmony_ci if (!newn->expr.nodes) 65662306a36Sopenharmony_ci goto error; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci newn->expr.len = orign->expr.len; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci rc = cond_dup_av_list(&newn->true_list, &orign->true_list, 66162306a36Sopenharmony_ci &newp->te_cond_avtab); 66262306a36Sopenharmony_ci if (rc) 66362306a36Sopenharmony_ci goto error; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci rc = cond_dup_av_list(&newn->false_list, &orign->false_list, 66662306a36Sopenharmony_ci &newp->te_cond_avtab); 66762306a36Sopenharmony_ci if (rc) 66862306a36Sopenharmony_ci goto error; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cierror: 67462306a36Sopenharmony_ci avtab_destroy(&newp->te_cond_avtab); 67562306a36Sopenharmony_ci cond_list_destroy(newp); 67662306a36Sopenharmony_ci return -ENOMEM; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int cond_bools_destroy(void *key, void *datum, void *args) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci /* key was not copied so no need to free here */ 68262306a36Sopenharmony_ci kfree(datum); 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci struct cond_bool_datum *datum; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci datum = kmemdup(orig->datum, sizeof(struct cond_bool_datum), 69162306a36Sopenharmony_ci GFP_KERNEL); 69262306a36Sopenharmony_ci if (!datum) 69362306a36Sopenharmony_ci return -ENOMEM; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci new->key = orig->key; /* No need to copy, never modified */ 69662306a36Sopenharmony_ci new->datum = datum; 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int cond_bools_index(void *key, void *datum, void *args) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct cond_bool_datum *booldatum, **cond_bool_array; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci booldatum = datum; 70562306a36Sopenharmony_ci cond_bool_array = args; 70662306a36Sopenharmony_ci cond_bool_array[booldatum->value - 1] = booldatum; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic int duplicate_policydb_bools(struct policydb *newdb, 71262306a36Sopenharmony_ci struct policydb *orig) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct cond_bool_datum **cond_bool_array; 71562306a36Sopenharmony_ci int rc; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci cond_bool_array = kmalloc_array(orig->p_bools.nprim, 71862306a36Sopenharmony_ci sizeof(*orig->bool_val_to_struct), 71962306a36Sopenharmony_ci GFP_KERNEL); 72062306a36Sopenharmony_ci if (!cond_bool_array) 72162306a36Sopenharmony_ci return -ENOMEM; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci rc = hashtab_duplicate(&newdb->p_bools.table, &orig->p_bools.table, 72462306a36Sopenharmony_ci cond_bools_copy, cond_bools_destroy, NULL); 72562306a36Sopenharmony_ci if (rc) { 72662306a36Sopenharmony_ci kfree(cond_bool_array); 72762306a36Sopenharmony_ci return -ENOMEM; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci hashtab_map(&newdb->p_bools.table, cond_bools_index, cond_bool_array); 73162306a36Sopenharmony_ci newdb->bool_val_to_struct = cond_bool_array; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci newdb->p_bools.nprim = orig->p_bools.nprim; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_civoid cond_policydb_destroy_dup(struct policydb *p) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci hashtab_map(&p->p_bools.table, cond_bools_destroy, NULL); 74162306a36Sopenharmony_ci hashtab_destroy(&p->p_bools.table); 74262306a36Sopenharmony_ci cond_policydb_destroy(p); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ciint cond_policydb_dup(struct policydb *new, struct policydb *orig) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci cond_policydb_init(new); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (duplicate_policydb_bools(new, orig)) 75062306a36Sopenharmony_ci return -ENOMEM; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (duplicate_policydb_cond_list(new, orig)) { 75362306a36Sopenharmony_ci cond_policydb_destroy_dup(new); 75462306a36Sopenharmony_ci return -ENOMEM; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci} 759