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