| /* Authors: Karl MacMillan <kmacmillan@tresys.com> |
| * Frank Mayer <mayerf@tresys.com> |
| * David Caplan <dac@tresys.com> |
| * |
| * Copyright (C) 2003 - 2005 Tresys Technology, LLC |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <stdlib.h> |
| |
| #include <sepol/policydb/flask_types.h> |
| #include <sepol/policydb/conditional.h> |
| |
| #include "private.h" |
| |
| /* move all type rules to top of t/f lists to help kernel on evaluation */ |
| static void cond_optimize(cond_av_list_t ** l) |
| { |
| cond_av_list_t *top, *p, *cur; |
| |
| top = p = cur = *l; |
| |
| while (cur) { |
| if ((cur->node->key.specified & AVTAB_TYPE) && (top != cur)) { |
| p->next = cur->next; |
| cur->next = top; |
| top = cur; |
| cur = p->next; |
| } else { |
| p = cur; |
| cur = cur->next; |
| } |
| } |
| *l = top; |
| } |
| |
| /* reorder t/f lists for kernel */ |
| void cond_optimize_lists(cond_list_t * cl) |
| { |
| cond_list_t *n; |
| |
| for (n = cl; n != NULL; n = n->next) { |
| cond_optimize(&n->true_list); |
| cond_optimize(&n->false_list); |
| } |
| } |
| |
| static int bool_present(unsigned int target, unsigned int bools[], |
| unsigned int num_bools) |
| { |
| unsigned int i = 0; |
| int ret = 1; |
| |
| if (num_bools > COND_MAX_BOOLS) { |
| return 0; |
| } |
| while (i < num_bools && target != bools[i]) |
| i++; |
| if (i == num_bools) |
| ret = 0; /* got to end w/o match */ |
| return ret; |
| } |
| |
| static int same_bools(cond_node_t * a, cond_node_t * b) |
| { |
| unsigned int i, x; |
| |
| x = a->nbools; |
| |
| /* same number of bools? */ |
| if (x != b->nbools) |
| return 0; |
| |
| /* make sure all the bools in a are also in b */ |
| for (i = 0; i < x; i++) |
| if (!bool_present(a->bool_ids[i], b->bool_ids, x)) |
| return 0; |
| return 1; |
| } |
| |
| /* |
| * Determine if two conditional expressions are equal. |
| */ |
| int cond_expr_equal(cond_node_t * a, cond_node_t * b) |
| { |
| cond_expr_t *cur_a, *cur_b; |
| |
| if (a == NULL || b == NULL) |
| return 0; |
| |
| if (a->nbools != b->nbools) |
| return 0; |
| |
| /* if exprs have <= COND_MAX_BOOLS we can check the precompute values |
| * for the expressions. |
| */ |
| if (a->nbools <= COND_MAX_BOOLS && b->nbools <= COND_MAX_BOOLS) { |
| if (!same_bools(a, b)) |
| return 0; |
| return (a->expr_pre_comp == b->expr_pre_comp); |
| } |
| |
| /* for long expressions we check for exactly the same expression */ |
| cur_a = a->expr; |
| cur_b = b->expr; |
| while (1) { |
| if (cur_a == NULL && cur_b == NULL) |
| return 1; |
| else if (cur_a == NULL || cur_b == NULL) |
| return 0; |
| if (cur_a->expr_type != cur_b->expr_type) |
| return 0; |
| if (cur_a->expr_type == COND_BOOL) { |
| if (cur_a->bool != cur_b->bool) |
| return 0; |
| } |
| cur_a = cur_a->next; |
| cur_b = cur_b->next; |
| } |
| return 1; |
| } |
| |
| /* Create a new conditional node, optionally copying |
| * the conditional expression from an existing node. |
| * If node is NULL then a new node will be created |
| * with no conditional expression. |
| */ |
| cond_node_t *cond_node_create(policydb_t * p, cond_node_t * node) |
| { |
| cond_node_t *new_node; |
| unsigned int i; |
| |
| new_node = (cond_node_t *)malloc(sizeof(cond_node_t)); |
| if (!new_node) { |
| return NULL; |
| } |
| memset(new_node, 0, sizeof(cond_node_t)); |
| |
| if (node) { |
| new_node->expr = cond_copy_expr(node->expr); |
| if (!new_node->expr) { |
| free(new_node); |
| return NULL; |
| } |
| new_node->cur_state = cond_evaluate_expr(p, new_node->expr); |
| new_node->nbools = node->nbools; |
| for (i = 0; i < min(node->nbools, COND_MAX_BOOLS); i++) |
| new_node->bool_ids[i] = node->bool_ids[i]; |
| new_node->expr_pre_comp = node->expr_pre_comp; |
| new_node->flags = node->flags; |
| } |
| |
| return new_node; |
| } |
| |
| /* Find a conditional (the needle) within a list of existing ones (the |
| * haystack) that has a matching expression. If found, return a |
| * pointer to the existing node, setting 'was_created' to 0. |
| * Otherwise create a new one and return it, setting 'was_created' to |
| * 1. */ |
| cond_node_t *cond_node_find(policydb_t * p, |
| cond_node_t * needle, cond_node_t * haystack, |
| int *was_created) |
| { |
| while (haystack) { |
| if (cond_expr_equal(needle, haystack)) { |
| *was_created = 0; |
| return haystack; |
| } |
| haystack = haystack->next; |
| } |
| *was_created = 1; |
| |
| return cond_node_create(p, needle); |
| } |
| |
| /* return either a pre-existing matching node or create a new node */ |
| cond_node_t *cond_node_search(policydb_t * p, cond_node_t * list, |
| cond_node_t * cn) |
| { |
| int was_created; |
| cond_node_t *result = cond_node_find(p, cn, list, &was_created); |
| if (result != NULL && was_created) { |
| /* add conditional node to policy list */ |
| result->next = p->cond_list; |
| p->cond_list = result; |
| } |
| return result; |
| } |
| |
| /* |
| * cond_evaluate_expr evaluates a conditional expr |
| * in reverse polish notation. It returns true (1), false (0), |
| * or undefined (-1). Undefined occurs when the expression |
| * exceeds the stack depth of COND_EXPR_MAXDEPTH. |
| */ |
| int cond_evaluate_expr(policydb_t * p, cond_expr_t * expr) |
| { |
| |
| cond_expr_t *cur; |
| int s[COND_EXPR_MAXDEPTH]; |
| int sp = -1; |
| |
| s[0] = -1; |
| |
| for (cur = expr; cur != NULL; cur = cur->next) { |
| switch (cur->expr_type) { |
| case COND_BOOL: |
| if (sp == (COND_EXPR_MAXDEPTH - 1)) |
| return -1; |
| sp++; |
| s[sp] = p->bool_val_to_struct[cur->bool - 1]->state; |
| break; |
| case COND_NOT: |
| if (sp < 0) |
| return -1; |
| s[sp] = !s[sp]; |
| break; |
| case COND_OR: |
| if (sp < 1) |
| return -1; |
| sp--; |
| s[sp] |= s[sp + 1]; |
| break; |
| case COND_AND: |
| if (sp < 1) |
| return -1; |
| sp--; |
| s[sp] &= s[sp + 1]; |
| break; |
| case COND_XOR: |
| if (sp < 1) |
| return -1; |
| sp--; |
| s[sp] ^= s[sp + 1]; |
| break; |
| case COND_EQ: |
| if (sp < 1) |
| return -1; |
| sp--; |
| s[sp] = (s[sp] == s[sp + 1]); |
| break; |
| case COND_NEQ: |
| if (sp < 1) |
| return -1; |
| sp--; |
| s[sp] = (s[sp] != s[sp + 1]); |
| break; |
| default: |
| return -1; |
| } |
| } |
| return s[0]; |
| } |
| |
| cond_expr_t *cond_copy_expr(cond_expr_t * expr) |
| { |
| cond_expr_t *cur, *head, *tail, *new_expr; |
| tail = head = NULL; |
| cur = expr; |
| while (cur) { |
| new_expr = (cond_expr_t *) malloc(sizeof(cond_expr_t)); |
| if (!new_expr) |
| goto free_head; |
| memset(new_expr, 0, sizeof(cond_expr_t)); |
| |
| new_expr->expr_type = cur->expr_type; |
| new_expr->bool = cur->bool; |
| |
| if (!head) |
| head = new_expr; |
| if (tail) |
| tail->next = new_expr; |
| tail = new_expr; |
| cur = cur->next; |
| } |
| return head; |
| |
| free_head: |
| while (head) { |
| tail = head->next; |
| free(head); |
| head = tail; |
| } |
| return NULL; |
| } |
| |
| /* |
| * evaluate_cond_node evaluates the conditional stored in |
| * a cond_node_t and if the result is different than the |
| * current state of the node it sets the rules in the true/false |
| * list appropriately. If the result of the expression is undefined |
| * all of the rules are disabled for safety. |
| */ |
| static int evaluate_cond_node(policydb_t * p, cond_node_t * node) |
| { |
| int new_state; |
| cond_av_list_t *cur; |
| |
| new_state = cond_evaluate_expr(p, node->expr); |
| if (new_state != node->cur_state) { |
| node->cur_state = new_state; |
| if (new_state == -1) |
| printf |
| ("expression result was undefined - disabling all rules.\n"); |
| /* turn the rules on or off */ |
| for (cur = node->true_list; cur != NULL; cur = cur->next) { |
| if (new_state <= 0) { |
| cur->node->key.specified &= ~AVTAB_ENABLED; |
| } else { |
| cur->node->key.specified |= AVTAB_ENABLED; |
| } |
| } |
| |
| for (cur = node->false_list; cur != NULL; cur = cur->next) { |
| /* -1 or 1 */ |
| if (new_state) { |
| cur->node->key.specified &= ~AVTAB_ENABLED; |
| } else { |
| cur->node->key.specified |= AVTAB_ENABLED; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /* precompute and simplify an expression if possible. If left with !expression, change |
| * to expression and switch t and f. precompute expression for expressions with limited |
| * number of bools. |
| */ |
| int cond_normalize_expr(policydb_t * p, cond_node_t * cn) |
| { |
| cond_expr_t *ne, *e; |
| cond_av_list_t *tmp; |
| unsigned int i, j, orig_value[COND_MAX_BOOLS]; |
| int k; |
| uint32_t test = 0x0; |
| avrule_t *tmp2; |
| |
| cn->nbools = 0; |
| |
| memset(cn->bool_ids, 0, sizeof(cn->bool_ids)); |
| cn->expr_pre_comp = 0x0; |
| |
| /* take care of !expr case */ |
| ne = NULL; |
| e = cn->expr; |
| |
| /* becuase it's RPN look at last element */ |
| while (e->next != NULL) { |
| ne = e; |
| e = e->next; |
| } |
| if (e->expr_type == COND_NOT) { |
| if (ne) { |
| ne->next = NULL; |
| } else { /* ne should never be NULL */ |
| printf |
| ("Found expr with no bools and only a ! - this should never happen.\n"); |
| return -1; |
| } |
| /* swap the true and false lists */ |
| tmp = cn->true_list; |
| cn->true_list = cn->false_list; |
| cn->false_list = tmp; |
| tmp2 = cn->avtrue_list; |
| cn->avtrue_list = cn->avfalse_list; |
| cn->avfalse_list = tmp2; |
| |
| /* free the "not" node in the list */ |
| free(e); |
| } |
| |
| /* find all the bools in the expression */ |
| for (e = cn->expr; e != NULL; e = e->next) { |
| switch (e->expr_type) { |
| case COND_BOOL: |
| i = 0; |
| /* see if we've already seen this bool */ |
| if (!bool_present(e->bool, cn->bool_ids, cn->nbools)) { |
| /* count em all but only record up to COND_MAX_BOOLS */ |
| if (cn->nbools < COND_MAX_BOOLS) |
| cn->bool_ids[cn->nbools++] = e->bool; |
| else |
| cn->nbools++; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* only precompute for exprs with <= COND_AX_BOOLS */ |
| if (cn->nbools <= COND_MAX_BOOLS) { |
| /* save the default values for the bools so we can play with them */ |
| for (i = 0; i < cn->nbools; i++) { |
| orig_value[i] = |
| p->bool_val_to_struct[cn->bool_ids[i] - 1]->state; |
| } |
| |
| /* loop through all possible combinations of values for bools in expression */ |
| for (test = 0x0; test < (0x1U << cn->nbools); test++) { |
| /* temporarily set the value for all the bools in the |
| * expression using the corr. bit in test */ |
| for (j = 0; j < cn->nbools; j++) { |
| p->bool_val_to_struct[cn->bool_ids[j] - |
| 1]->state = |
| (test & (0x1 << j)) ? 1 : 0; |
| } |
| k = cond_evaluate_expr(p, cn->expr); |
| if (k == -1) { |
| printf |
| ("While testing expression, expression result " |
| "was undefined - this should never happen.\n"); |
| return -1; |
| } |
| /* set the bit if expression evaluates true */ |
| if (k) |
| cn->expr_pre_comp |= 0x1 << test; |
| } |
| |
| /* restore bool default values */ |
| for (i = 0; i < cn->nbools; i++) |
| p->bool_val_to_struct[cn->bool_ids[i] - 1]->state = |
| orig_value[i]; |
| } |
| return 0; |
| } |
| |
| int evaluate_conds(policydb_t * p) |
| { |
| int ret; |
| cond_node_t *cur; |
| |
| for (cur = p->cond_list; cur != NULL; cur = cur->next) { |
| ret = evaluate_cond_node(p, cur); |
| if (ret) |
| return ret; |
| } |
| return 0; |
| } |
| |
| int cond_policydb_init(policydb_t * p) |
| { |
| p->bool_val_to_struct = NULL; |
| p->cond_list = NULL; |
| if (avtab_init(&p->te_cond_avtab)) |
| return -1; |
| |
| return 0; |
| } |
| |
| void cond_av_list_destroy(cond_av_list_t * list) |
| { |
| cond_av_list_t *cur, *next; |
| for (cur = list; cur != NULL; cur = next) { |
| next = cur->next; |
| /* the avtab_ptr_t node is destroy by the avtab */ |
| free(cur); |
| } |
| } |
| |
| void cond_expr_destroy(cond_expr_t * expr) |
| { |
| cond_expr_t *cur_expr, *next_expr; |
| |
| if (!expr) |
| return; |
| |
| for (cur_expr = expr; cur_expr != NULL; cur_expr = next_expr) { |
| next_expr = cur_expr->next; |
| free(cur_expr); |
| } |
| } |
| |
| void cond_node_destroy(cond_node_t * node) |
| { |
| if (!node) |
| return; |
| |
| cond_expr_destroy(node->expr); |
| avrule_list_destroy(node->avtrue_list); |
| avrule_list_destroy(node->avfalse_list); |
| cond_av_list_destroy(node->true_list); |
| cond_av_list_destroy(node->false_list); |
| } |
| |
| void cond_list_destroy(cond_list_t * list) |
| { |
| cond_node_t *next, *cur; |
| |
| if (list == NULL) |
| return; |
| |
| for (cur = list; cur != NULL; cur = next) { |
| next = cur->next; |
| cond_node_destroy(cur); |
| free(cur); |
| } |
| } |
| |
| void cond_policydb_destroy(policydb_t * p) |
| { |
| if (p->bool_val_to_struct != NULL) |
| free(p->bool_val_to_struct); |
| avtab_destroy(&p->te_cond_avtab); |
| cond_list_destroy(p->cond_list); |
| } |
| |
| int cond_init_bool_indexes(policydb_t * p) |
| { |
| if (p->bool_val_to_struct) |
| free(p->bool_val_to_struct); |
| p->bool_val_to_struct = (cond_bool_datum_t **) |
| malloc(p->p_bools.nprim * sizeof(cond_bool_datum_t *)); |
| if (!p->bool_val_to_struct) |
| return -1; |
| return 0; |
| } |
| |
| int cond_destroy_bool(hashtab_key_t key, hashtab_datum_t datum, void *p |
| __attribute__ ((unused))) |
| { |
| if (key) |
| free(key); |
| free(datum); |
| return 0; |
| } |
| |
| int cond_index_bool(hashtab_key_t key, hashtab_datum_t datum, void *datap) |
| { |
| policydb_t *p; |
| cond_bool_datum_t *booldatum; |
| |
| booldatum = datum; |
| p = datap; |
| |
| if (!booldatum->s.value || booldatum->s.value > p->p_bools.nprim) |
| return -EINVAL; |
| |
| p->p_bool_val_to_name[booldatum->s.value - 1] = key; |
| p->bool_val_to_struct[booldatum->s.value - 1] = booldatum; |
| |
| return 0; |
| } |
| |
| static int bool_isvalid(cond_bool_datum_t * b) |
| { |
| if (!(b->state == 0 || b->state == 1)) |
| return 0; |
| return 1; |
| } |
| |
| int cond_read_bool(policydb_t * p, |
| hashtab_t h, |
| struct policy_file *fp) |
| { |
| char *key = 0; |
| cond_bool_datum_t *booldatum; |
| uint32_t buf[3], len; |
| int rc; |
| |
| booldatum = malloc(sizeof(cond_bool_datum_t)); |
| if (!booldatum) |
| return -1; |
| memset(booldatum, 0, sizeof(cond_bool_datum_t)); |
| |
| rc = next_entry(buf, fp, sizeof(uint32_t) * 3); |
| if (rc < 0) |
| goto err; |
| |
| booldatum->s.value = le32_to_cpu(buf[0]); |
| booldatum->state = le32_to_cpu(buf[1]); |
| |
| if (!bool_isvalid(booldatum)) |
| goto err; |
| |
| len = le32_to_cpu(buf[2]); |
| |
| key = malloc(len + 1); |
| if (!key) |
| goto err; |
| rc = next_entry(key, fp, len); |
| if (rc < 0) |
| goto err; |
| key[len] = 0; |
| |
| if (p->policy_type != POLICY_KERN && |
| p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { |
| rc = next_entry(buf, fp, sizeof(uint32_t)); |
| if (rc < 0) |
| goto err; |
| booldatum->flags = le32_to_cpu(buf[0]); |
| } |
| |
| if (hashtab_insert(h, key, booldatum)) |
| goto err; |
| |
| return 0; |
| err: |
| cond_destroy_bool(key, booldatum, 0); |
| return -1; |
| } |
| |
| struct cond_insertf_data { |
| struct policydb *p; |
| cond_av_list_t *other; |
| cond_av_list_t *head; |
| cond_av_list_t *tail; |
| }; |
| |
| static int cond_insertf(avtab_t * a |
| __attribute__ ((unused)), avtab_key_t * k, |
| avtab_datum_t * d, void *ptr) |
| { |
| struct cond_insertf_data *data = ptr; |
| struct policydb *p = data->p; |
| cond_av_list_t *other = data->other, *list, *cur; |
| avtab_ptr_t node_ptr; |
| uint8_t found; |
| |
| /* |
| * For type rules we have to make certain there aren't any |
| * conflicting rules by searching the te_avtab and the |
| * cond_te_avtab. |
| */ |
| if (k->specified & AVTAB_TYPE) { |
| if (avtab_search(&p->te_avtab, k)) { |
| printf |
| ("security: type rule already exists outside of a conditional."); |
| goto err; |
| } |
| /* |
| * If we are reading the false list other will be a pointer to |
| * the true list. We can have duplicate entries if there is only |
| * 1 other entry and it is in our true list. |
| * |
| * If we are reading the true list (other == NULL) there shouldn't |
| * be any other entries. |
| */ |
| if (other) { |
| node_ptr = avtab_search_node(&p->te_cond_avtab, k); |
| if (node_ptr) { |
| if (avtab_search_node_next |
| (node_ptr, k->specified)) { |
| printf |
| ("security: too many conflicting type rules."); |
| goto err; |
| } |
| found = 0; |
| for (cur = other; cur != NULL; cur = cur->next) { |
| if (cur->node == node_ptr) { |
| found = 1; |
| break; |
| } |
| } |
| if (!found) { |
| printf |
| ("security: conflicting type rules.\n"); |
| goto err; |
| } |
| } |
| } else { |
| if (avtab_search(&p->te_cond_avtab, k)) { |
| printf |
| ("security: conflicting type rules when adding type rule for true.\n"); |
| goto err; |
| } |
| } |
| } |
| |
| node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); |
| if (!node_ptr) { |
| printf("security: could not insert rule."); |
| goto err; |
| } |
| node_ptr->parse_context = (void *)1; |
| |
| list = malloc(sizeof(cond_av_list_t)); |
| if (!list) |
| goto err; |
| memset(list, 0, sizeof(cond_av_list_t)); |
| |
| list->node = node_ptr; |
| if (!data->head) |
| data->head = list; |
| else |
| data->tail->next = list; |
| data->tail = list; |
| return 0; |
| |
| err: |
| cond_av_list_destroy(data->head); |
| data->head = NULL; |
| return -1; |
| } |
| |
| static int cond_read_av_list(policydb_t * p, void *fp, |
| cond_av_list_t ** ret_list, cond_av_list_t * other) |
| { |
| unsigned int i; |
| int rc; |
| uint32_t buf[1], len; |
| struct cond_insertf_data data; |
| |
| *ret_list = NULL; |
| |
| len = 0; |
| rc = next_entry(buf, fp, sizeof(uint32_t)); |
| if (rc < 0) |
| return -1; |
| |
| len = le32_to_cpu(buf[0]); |
| if (len == 0) { |
| return 0; |
| } |
| |
| data.p = p; |
| data.other = other; |
| data.head = NULL; |
| data.tail = NULL; |
| for (i = 0; i < len; i++) { |
| rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab, |
| cond_insertf, &data); |
| if (rc) |
| return rc; |
| |
| } |
| |
| *ret_list = data.head; |
| return 0; |
| } |
| |
| static int expr_isvalid(policydb_t * p, cond_expr_t * expr) |
| { |
| if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { |
| printf |
| ("security: conditional expressions uses unknown operator.\n"); |
| return 0; |
| } |
| |
| if (expr->bool > p->p_bools.nprim) { |
| printf |
| ("security: conditional expressions uses unknown bool.\n"); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int cond_read_node(policydb_t * p, cond_node_t * node, void *fp) |
| { |
| uint32_t buf[2]; |
| int len, i, rc; |
| cond_expr_t *expr = NULL, *last = NULL; |
| |
| rc = next_entry(buf, fp, sizeof(uint32_t)); |
| if (rc < 0) |
| goto err; |
| |
| node->cur_state = le32_to_cpu(buf[0]); |
| |
| len = 0; |
| rc = next_entry(buf, fp, sizeof(uint32_t)); |
| if (rc < 0) |
| goto err; |
| |
| /* expr */ |
| len = le32_to_cpu(buf[0]); |
| |
| for (i = 0; i < len; i++) { |
| rc = next_entry(buf, fp, sizeof(uint32_t) * 2); |
| if (rc < 0) |
| goto err; |
| |
| expr = malloc(sizeof(cond_expr_t)); |
| if (!expr) { |
| goto err; |
| } |
| memset(expr, 0, sizeof(cond_expr_t)); |
| |
| expr->expr_type = le32_to_cpu(buf[0]); |
| expr->bool = le32_to_cpu(buf[1]); |
| |
| if (!expr_isvalid(p, expr)) { |
| free(expr); |
| goto err; |
| } |
| |
| if (i == 0) { |
| node->expr = expr; |
| } else { |
| last->next = expr; |
| } |
| last = expr; |
| } |
| |
| if (p->policy_type == POLICY_KERN) { |
| if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) |
| goto err; |
| if (cond_read_av_list(p, fp, &node->false_list, node->true_list) |
| != 0) |
| goto err; |
| } else { |
| if (avrule_read_list(p, &node->avtrue_list, fp)) |
| goto err; |
| if (avrule_read_list(p, &node->avfalse_list, fp)) |
| goto err; |
| } |
| |
| if (p->policy_type != POLICY_KERN && |
| p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { |
| rc = next_entry(buf, fp, sizeof(uint32_t)); |
| if (rc < 0) |
| goto err; |
| node->flags = le32_to_cpu(buf[0]); |
| } |
| |
| return 0; |
| err: |
| cond_node_destroy(node); |
| free(node); |
| return -1; |
| } |
| |
| int cond_read_list(policydb_t * p, cond_list_t ** list, void *fp) |
| { |
| cond_node_t *node, *last = NULL; |
| uint32_t buf[1]; |
| int i, len, rc; |
| |
| rc = next_entry(buf, fp, sizeof(uint32_t)); |
| if (rc < 0) |
| return -1; |
| |
| len = le32_to_cpu(buf[0]); |
| |
| rc = avtab_alloc(&p->te_cond_avtab, p->te_avtab.nel); |
| if (rc) |
| goto err; |
| |
| for (i = 0; i < len; i++) { |
| node = malloc(sizeof(cond_node_t)); |
| if (!node) |
| goto err; |
| memset(node, 0, sizeof(cond_node_t)); |
| |
| if (cond_read_node(p, node, fp) != 0) |
| goto err; |
| |
| if (i == 0) { |
| *list = node; |
| } else { |
| last->next = node; |
| } |
| last = node; |
| } |
| return 0; |
| err: |
| return -1; |
| } |
| |
| /* Determine whether additional permissions are granted by the conditional |
| * av table, and if so, add them to the result |
| */ |
| void cond_compute_av(avtab_t * ctab, avtab_key_t * key, |
| struct sepol_av_decision *avd) |
| { |
| avtab_ptr_t node; |
| |
| if (!ctab || !key || !avd) |
| return; |
| |
| for (node = avtab_search_node(ctab, key); node != NULL; |
| node = avtab_search_node_next(node, key->specified)) { |
| if ((uint16_t) (AVTAB_ALLOWED | AVTAB_ENABLED) == |
| (node->key.specified & (AVTAB_ALLOWED | AVTAB_ENABLED))) |
| avd->allowed |= node->datum.data; |
| if ((uint16_t) (AVTAB_AUDITDENY | AVTAB_ENABLED) == |
| (node->key.specified & (AVTAB_AUDITDENY | AVTAB_ENABLED))) |
| /* Since a '0' in an auditdeny mask represents a |
| * permission we do NOT want to audit (dontaudit), we use |
| * the '&' operand to ensure that all '0's in the mask |
| * are retained (much unlike the allow and auditallow cases). |
| */ |
| avd->auditdeny &= node->datum.data; |
| if ((uint16_t) (AVTAB_AUDITALLOW | AVTAB_ENABLED) == |
| (node->key.specified & (AVTAB_AUDITALLOW | AVTAB_ENABLED))) |
| avd->auditallow |= node->datum.data; |
| } |
| return; |
| } |
| |
| avtab_datum_t *cond_av_list_search(avtab_key_t * key, |
| cond_av_list_t * cond_list) |
| { |
| |
| cond_av_list_t *cur_av; |
| |
| for (cur_av = cond_list; cur_av != NULL; cur_av = cur_av->next) { |
| |
| if (cur_av->node->key.source_type == key->source_type && |
| cur_av->node->key.target_type == key->target_type && |
| cur_av->node->key.target_class == key->target_class) |
| |
| return &cur_av->node->datum; |
| |
| } |
| return NULL; |
| |
| } |