162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Implementation of the access vector table type.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *	Added conditional policy language extensions
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright (C) 2003 Tresys Technology, LLC
1262306a36Sopenharmony_ci *	This program is free software; you can redistribute it and/or modify
1362306a36Sopenharmony_ci *	it under the terms of the GNU General Public License as published by
1462306a36Sopenharmony_ci *	the Free Software Foundation, version 2.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
1762306a36Sopenharmony_ci *	Tuned number of hash slots for avtab to reduce memory usage
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/errno.h>
2362306a36Sopenharmony_ci#include "avtab.h"
2462306a36Sopenharmony_ci#include "policydb.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct kmem_cache *avtab_node_cachep __ro_after_init;
2762306a36Sopenharmony_cistatic struct kmem_cache *avtab_xperms_cachep __ro_after_init;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* Based on MurmurHash3, written by Austin Appleby and placed in the
3062306a36Sopenharmony_ci * public domain.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistatic inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	static const u32 c1 = 0xcc9e2d51;
3562306a36Sopenharmony_ci	static const u32 c2 = 0x1b873593;
3662306a36Sopenharmony_ci	static const u32 r1 = 15;
3762306a36Sopenharmony_ci	static const u32 r2 = 13;
3862306a36Sopenharmony_ci	static const u32 m  = 5;
3962306a36Sopenharmony_ci	static const u32 n  = 0xe6546b64;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	u32 hash = 0;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define mix(input) do { \
4462306a36Sopenharmony_ci		u32 v = input; \
4562306a36Sopenharmony_ci		v *= c1; \
4662306a36Sopenharmony_ci		v = (v << r1) | (v >> (32 - r1)); \
4762306a36Sopenharmony_ci		v *= c2; \
4862306a36Sopenharmony_ci		hash ^= v; \
4962306a36Sopenharmony_ci		hash = (hash << r2) | (hash >> (32 - r2)); \
5062306a36Sopenharmony_ci		hash = hash * m + n; \
5162306a36Sopenharmony_ci	} while (0)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	mix(keyp->target_class);
5462306a36Sopenharmony_ci	mix(keyp->target_type);
5562306a36Sopenharmony_ci	mix(keyp->source_type);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#undef mix
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	hash ^= hash >> 16;
6062306a36Sopenharmony_ci	hash *= 0x85ebca6b;
6162306a36Sopenharmony_ci	hash ^= hash >> 13;
6262306a36Sopenharmony_ci	hash *= 0xc2b2ae35;
6362306a36Sopenharmony_ci	hash ^= hash >> 16;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return hash & mask;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct avtab_node*
6962306a36Sopenharmony_ciavtab_insert_node(struct avtab *h, u32 hvalue,
7062306a36Sopenharmony_ci		  struct avtab_node *prev,
7162306a36Sopenharmony_ci		  const struct avtab_key *key, const struct avtab_datum *datum)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct avtab_node *newnode;
7462306a36Sopenharmony_ci	struct avtab_extended_perms *xperms;
7562306a36Sopenharmony_ci	newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
7662306a36Sopenharmony_ci	if (newnode == NULL)
7762306a36Sopenharmony_ci		return NULL;
7862306a36Sopenharmony_ci	newnode->key = *key;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (key->specified & AVTAB_XPERMS) {
8162306a36Sopenharmony_ci		xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL);
8262306a36Sopenharmony_ci		if (xperms == NULL) {
8362306a36Sopenharmony_ci			kmem_cache_free(avtab_node_cachep, newnode);
8462306a36Sopenharmony_ci			return NULL;
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci		*xperms = *(datum->u.xperms);
8762306a36Sopenharmony_ci		newnode->datum.u.xperms = xperms;
8862306a36Sopenharmony_ci	} else {
8962306a36Sopenharmony_ci		newnode->datum.u.data = datum->u.data;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (prev) {
9362306a36Sopenharmony_ci		newnode->next = prev->next;
9462306a36Sopenharmony_ci		prev->next = newnode;
9562306a36Sopenharmony_ci	} else {
9662306a36Sopenharmony_ci		struct avtab_node **n = &h->htable[hvalue];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		newnode->next = *n;
9962306a36Sopenharmony_ci		*n = newnode;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	h->nel++;
10362306a36Sopenharmony_ci	return newnode;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int avtab_insert(struct avtab *h, const struct avtab_key *key,
10762306a36Sopenharmony_ci			const struct avtab_datum *datum)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u32 hvalue;
11062306a36Sopenharmony_ci	struct avtab_node *prev, *cur, *newnode;
11162306a36Sopenharmony_ci	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (!h || !h->nslot || h->nel == U32_MAX)
11462306a36Sopenharmony_ci		return -EINVAL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	hvalue = avtab_hash(key, h->mask);
11762306a36Sopenharmony_ci	for (prev = NULL, cur = h->htable[hvalue];
11862306a36Sopenharmony_ci	     cur;
11962306a36Sopenharmony_ci	     prev = cur, cur = cur->next) {
12062306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
12162306a36Sopenharmony_ci		    key->target_type == cur->key.target_type &&
12262306a36Sopenharmony_ci		    key->target_class == cur->key.target_class &&
12362306a36Sopenharmony_ci		    (specified & cur->key.specified)) {
12462306a36Sopenharmony_ci			/* extended perms may not be unique */
12562306a36Sopenharmony_ci			if (specified & AVTAB_XPERMS)
12662306a36Sopenharmony_ci				break;
12762306a36Sopenharmony_ci			return -EEXIST;
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci		if (key->source_type < cur->key.source_type)
13062306a36Sopenharmony_ci			break;
13162306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
13262306a36Sopenharmony_ci		    key->target_type < cur->key.target_type)
13362306a36Sopenharmony_ci			break;
13462306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
13562306a36Sopenharmony_ci		    key->target_type == cur->key.target_type &&
13662306a36Sopenharmony_ci		    key->target_class < cur->key.target_class)
13762306a36Sopenharmony_ci			break;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	newnode = avtab_insert_node(h, hvalue, prev, key, datum);
14162306a36Sopenharmony_ci	if (!newnode)
14262306a36Sopenharmony_ci		return -ENOMEM;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* Unlike avtab_insert(), this function allow multiple insertions of the same
14862306a36Sopenharmony_ci * key/specified mask into the table, as needed by the conditional avtab.
14962306a36Sopenharmony_ci * It also returns a pointer to the node inserted.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistruct avtab_node *avtab_insert_nonunique(struct avtab *h,
15262306a36Sopenharmony_ci					  const struct avtab_key *key,
15362306a36Sopenharmony_ci					  const struct avtab_datum *datum)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	u32 hvalue;
15662306a36Sopenharmony_ci	struct avtab_node *prev, *cur;
15762306a36Sopenharmony_ci	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!h || !h->nslot || h->nel == U32_MAX)
16062306a36Sopenharmony_ci		return NULL;
16162306a36Sopenharmony_ci	hvalue = avtab_hash(key, h->mask);
16262306a36Sopenharmony_ci	for (prev = NULL, cur = h->htable[hvalue];
16362306a36Sopenharmony_ci	     cur;
16462306a36Sopenharmony_ci	     prev = cur, cur = cur->next) {
16562306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
16662306a36Sopenharmony_ci		    key->target_type == cur->key.target_type &&
16762306a36Sopenharmony_ci		    key->target_class == cur->key.target_class &&
16862306a36Sopenharmony_ci		    (specified & cur->key.specified))
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci		if (key->source_type < cur->key.source_type)
17162306a36Sopenharmony_ci			break;
17262306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
17362306a36Sopenharmony_ci		    key->target_type < cur->key.target_type)
17462306a36Sopenharmony_ci			break;
17562306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
17662306a36Sopenharmony_ci		    key->target_type == cur->key.target_type &&
17762306a36Sopenharmony_ci		    key->target_class < cur->key.target_class)
17862306a36Sopenharmony_ci			break;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci	return avtab_insert_node(h, hvalue, prev, key, datum);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* This search function returns a node pointer, and can be used in
18462306a36Sopenharmony_ci * conjunction with avtab_search_next_node()
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_cistruct avtab_node *avtab_search_node(struct avtab *h,
18762306a36Sopenharmony_ci				     const struct avtab_key *key)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	u32 hvalue;
19062306a36Sopenharmony_ci	struct avtab_node *cur;
19162306a36Sopenharmony_ci	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (!h || !h->nslot)
19462306a36Sopenharmony_ci		return NULL;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	hvalue = avtab_hash(key, h->mask);
19762306a36Sopenharmony_ci	for (cur = h->htable[hvalue]; cur;
19862306a36Sopenharmony_ci	     cur = cur->next) {
19962306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
20062306a36Sopenharmony_ci		    key->target_type == cur->key.target_type &&
20162306a36Sopenharmony_ci		    key->target_class == cur->key.target_class &&
20262306a36Sopenharmony_ci		    (specified & cur->key.specified))
20362306a36Sopenharmony_ci			return cur;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		if (key->source_type < cur->key.source_type)
20662306a36Sopenharmony_ci			break;
20762306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
20862306a36Sopenharmony_ci		    key->target_type < cur->key.target_type)
20962306a36Sopenharmony_ci			break;
21062306a36Sopenharmony_ci		if (key->source_type == cur->key.source_type &&
21162306a36Sopenharmony_ci		    key->target_type == cur->key.target_type &&
21262306a36Sopenharmony_ci		    key->target_class < cur->key.target_class)
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	return NULL;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistruct avtab_node*
21962306a36Sopenharmony_ciavtab_search_node_next(struct avtab_node *node, u16 specified)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct avtab_node *cur;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!node)
22462306a36Sopenharmony_ci		return NULL;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
22762306a36Sopenharmony_ci	for (cur = node->next; cur; cur = cur->next) {
22862306a36Sopenharmony_ci		if (node->key.source_type == cur->key.source_type &&
22962306a36Sopenharmony_ci		    node->key.target_type == cur->key.target_type &&
23062306a36Sopenharmony_ci		    node->key.target_class == cur->key.target_class &&
23162306a36Sopenharmony_ci		    (specified & cur->key.specified))
23262306a36Sopenharmony_ci			return cur;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		if (node->key.source_type < cur->key.source_type)
23562306a36Sopenharmony_ci			break;
23662306a36Sopenharmony_ci		if (node->key.source_type == cur->key.source_type &&
23762306a36Sopenharmony_ci		    node->key.target_type < cur->key.target_type)
23862306a36Sopenharmony_ci			break;
23962306a36Sopenharmony_ci		if (node->key.source_type == cur->key.source_type &&
24062306a36Sopenharmony_ci		    node->key.target_type == cur->key.target_type &&
24162306a36Sopenharmony_ci		    node->key.target_class < cur->key.target_class)
24262306a36Sopenharmony_ci			break;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	return NULL;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_civoid avtab_destroy(struct avtab *h)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	u32 i;
25062306a36Sopenharmony_ci	struct avtab_node *cur, *temp;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!h)
25362306a36Sopenharmony_ci		return;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	for (i = 0; i < h->nslot; i++) {
25662306a36Sopenharmony_ci		cur = h->htable[i];
25762306a36Sopenharmony_ci		while (cur) {
25862306a36Sopenharmony_ci			temp = cur;
25962306a36Sopenharmony_ci			cur = cur->next;
26062306a36Sopenharmony_ci			if (temp->key.specified & AVTAB_XPERMS)
26162306a36Sopenharmony_ci				kmem_cache_free(avtab_xperms_cachep,
26262306a36Sopenharmony_ci						temp->datum.u.xperms);
26362306a36Sopenharmony_ci			kmem_cache_free(avtab_node_cachep, temp);
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	kvfree(h->htable);
26762306a36Sopenharmony_ci	h->htable = NULL;
26862306a36Sopenharmony_ci	h->nel = 0;
26962306a36Sopenharmony_ci	h->nslot = 0;
27062306a36Sopenharmony_ci	h->mask = 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_civoid avtab_init(struct avtab *h)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	h->htable = NULL;
27662306a36Sopenharmony_ci	h->nel = 0;
27762306a36Sopenharmony_ci	h->nslot = 0;
27862306a36Sopenharmony_ci	h->mask = 0;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic int avtab_alloc_common(struct avtab *h, u32 nslot)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	if (!nslot)
28462306a36Sopenharmony_ci		return 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL);
28762306a36Sopenharmony_ci	if (!h->htable)
28862306a36Sopenharmony_ci		return -ENOMEM;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	h->nslot = nslot;
29162306a36Sopenharmony_ci	h->mask = nslot - 1;
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciint avtab_alloc(struct avtab *h, u32 nrules)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int rc;
29862306a36Sopenharmony_ci	u32 nslot = 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (nrules != 0) {
30162306a36Sopenharmony_ci		u32 shift = 1;
30262306a36Sopenharmony_ci		u32 work = nrules >> 3;
30362306a36Sopenharmony_ci		while (work) {
30462306a36Sopenharmony_ci			work >>= 1;
30562306a36Sopenharmony_ci			shift++;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci		nslot = 1 << shift;
30862306a36Sopenharmony_ci		if (nslot > MAX_AVTAB_HASH_BUCKETS)
30962306a36Sopenharmony_ci			nslot = MAX_AVTAB_HASH_BUCKETS;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		rc = avtab_alloc_common(h, nslot);
31262306a36Sopenharmony_ci		if (rc)
31362306a36Sopenharmony_ci			return rc;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	pr_debug("SELinux: %d avtab hash slots, %d rules.\n", nslot, nrules);
31762306a36Sopenharmony_ci	return 0;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ciint avtab_alloc_dup(struct avtab *new, const struct avtab *orig)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return avtab_alloc_common(new, orig->nslot);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_DEBUG
32662306a36Sopenharmony_civoid avtab_hash_eval(struct avtab *h, const char *tag)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	u32 i, chain_len, slots_used, max_chain_len;
32962306a36Sopenharmony_ci	unsigned long long chain2_len_sum;
33062306a36Sopenharmony_ci	struct avtab_node *cur;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	slots_used = 0;
33362306a36Sopenharmony_ci	max_chain_len = 0;
33462306a36Sopenharmony_ci	chain2_len_sum = 0;
33562306a36Sopenharmony_ci	for (i = 0; i < h->nslot; i++) {
33662306a36Sopenharmony_ci		cur = h->htable[i];
33762306a36Sopenharmony_ci		if (cur) {
33862306a36Sopenharmony_ci			slots_used++;
33962306a36Sopenharmony_ci			chain_len = 0;
34062306a36Sopenharmony_ci			while (cur) {
34162306a36Sopenharmony_ci				chain_len++;
34262306a36Sopenharmony_ci				cur = cur->next;
34362306a36Sopenharmony_ci			}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci			if (chain_len > max_chain_len)
34662306a36Sopenharmony_ci				max_chain_len = chain_len;
34762306a36Sopenharmony_ci			chain2_len_sum += (unsigned long long)chain_len * chain_len;
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	pr_debug("SELinux: %s:  %d entries and %d/%d buckets used, "
35262306a36Sopenharmony_ci	       "longest chain length %d sum of chain length^2 %llu\n",
35362306a36Sopenharmony_ci	       tag, h->nel, slots_used, h->nslot, max_chain_len,
35462306a36Sopenharmony_ci	       chain2_len_sum);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic const uint16_t spec_order[] = {
35962306a36Sopenharmony_ci	AVTAB_ALLOWED,
36062306a36Sopenharmony_ci	AVTAB_AUDITDENY,
36162306a36Sopenharmony_ci	AVTAB_AUDITALLOW,
36262306a36Sopenharmony_ci	AVTAB_TRANSITION,
36362306a36Sopenharmony_ci	AVTAB_CHANGE,
36462306a36Sopenharmony_ci	AVTAB_MEMBER,
36562306a36Sopenharmony_ci	AVTAB_XPERMS_ALLOWED,
36662306a36Sopenharmony_ci	AVTAB_XPERMS_AUDITALLOW,
36762306a36Sopenharmony_ci	AVTAB_XPERMS_DONTAUDIT
36862306a36Sopenharmony_ci};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ciint avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
37162306a36Sopenharmony_ci		    int (*insertf)(struct avtab *a, const struct avtab_key *k,
37262306a36Sopenharmony_ci				   const struct avtab_datum *d, void *p),
37362306a36Sopenharmony_ci		    void *p)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	__le16 buf16[4];
37662306a36Sopenharmony_ci	u16 enabled;
37762306a36Sopenharmony_ci	u32 items, items2, val, i;
37862306a36Sopenharmony_ci	struct avtab_key key;
37962306a36Sopenharmony_ci	struct avtab_datum datum;
38062306a36Sopenharmony_ci	struct avtab_extended_perms xperms;
38162306a36Sopenharmony_ci	__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
38262306a36Sopenharmony_ci	int rc;
38362306a36Sopenharmony_ci	unsigned int set, vers = pol->policyvers;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	memset(&key, 0, sizeof(struct avtab_key));
38662306a36Sopenharmony_ci	memset(&datum, 0, sizeof(struct avtab_datum));
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (vers < POLICYDB_VERSION_AVTAB) {
38962306a36Sopenharmony_ci		rc = next_entry(buf32, fp, sizeof(u32));
39062306a36Sopenharmony_ci		if (rc) {
39162306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated entry\n");
39262306a36Sopenharmony_ci			return rc;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci		items2 = le32_to_cpu(buf32[0]);
39562306a36Sopenharmony_ci		if (items2 > ARRAY_SIZE(buf32)) {
39662306a36Sopenharmony_ci			pr_err("SELinux: avtab: entry overflow\n");
39762306a36Sopenharmony_ci			return -EINVAL;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci		rc = next_entry(buf32, fp, sizeof(u32)*items2);
40162306a36Sopenharmony_ci		if (rc) {
40262306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated entry\n");
40362306a36Sopenharmony_ci			return rc;
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci		items = 0;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		val = le32_to_cpu(buf32[items++]);
40862306a36Sopenharmony_ci		key.source_type = (u16)val;
40962306a36Sopenharmony_ci		if (key.source_type != val) {
41062306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated source type\n");
41162306a36Sopenharmony_ci			return -EINVAL;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci		val = le32_to_cpu(buf32[items++]);
41462306a36Sopenharmony_ci		key.target_type = (u16)val;
41562306a36Sopenharmony_ci		if (key.target_type != val) {
41662306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated target type\n");
41762306a36Sopenharmony_ci			return -EINVAL;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci		val = le32_to_cpu(buf32[items++]);
42062306a36Sopenharmony_ci		key.target_class = (u16)val;
42162306a36Sopenharmony_ci		if (key.target_class != val) {
42262306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated target class\n");
42362306a36Sopenharmony_ci			return -EINVAL;
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		val = le32_to_cpu(buf32[items++]);
42762306a36Sopenharmony_ci		enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
43062306a36Sopenharmony_ci			pr_err("SELinux: avtab: null entry\n");
43162306a36Sopenharmony_ci			return -EINVAL;
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci		if ((val & AVTAB_AV) &&
43462306a36Sopenharmony_ci		    (val & AVTAB_TYPE)) {
43562306a36Sopenharmony_ci			pr_err("SELinux: avtab: entry has both access vectors and types\n");
43662306a36Sopenharmony_ci			return -EINVAL;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci		if (val & AVTAB_XPERMS) {
43962306a36Sopenharmony_ci			pr_err("SELinux: avtab: entry has extended permissions\n");
44062306a36Sopenharmony_ci			return -EINVAL;
44162306a36Sopenharmony_ci		}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
44462306a36Sopenharmony_ci			if (val & spec_order[i]) {
44562306a36Sopenharmony_ci				key.specified = spec_order[i] | enabled;
44662306a36Sopenharmony_ci				datum.u.data = le32_to_cpu(buf32[items++]);
44762306a36Sopenharmony_ci				rc = insertf(a, &key, &datum, p);
44862306a36Sopenharmony_ci				if (rc)
44962306a36Sopenharmony_ci					return rc;
45062306a36Sopenharmony_ci			}
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		if (items != items2) {
45462306a36Sopenharmony_ci			pr_err("SELinux: avtab: entry only had %d items, expected %d\n",
45562306a36Sopenharmony_ci			       items2, items);
45662306a36Sopenharmony_ci			return -EINVAL;
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci		return 0;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	rc = next_entry(buf16, fp, sizeof(u16)*4);
46262306a36Sopenharmony_ci	if (rc) {
46362306a36Sopenharmony_ci		pr_err("SELinux: avtab: truncated entry\n");
46462306a36Sopenharmony_ci		return rc;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	items = 0;
46862306a36Sopenharmony_ci	key.source_type = le16_to_cpu(buf16[items++]);
46962306a36Sopenharmony_ci	key.target_type = le16_to_cpu(buf16[items++]);
47062306a36Sopenharmony_ci	key.target_class = le16_to_cpu(buf16[items++]);
47162306a36Sopenharmony_ci	key.specified = le16_to_cpu(buf16[items++]);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (!policydb_type_isvalid(pol, key.source_type) ||
47462306a36Sopenharmony_ci	    !policydb_type_isvalid(pol, key.target_type) ||
47562306a36Sopenharmony_ci	    !policydb_class_isvalid(pol, key.target_class)) {
47662306a36Sopenharmony_ci		pr_err("SELinux: avtab: invalid type or class\n");
47762306a36Sopenharmony_ci		return -EINVAL;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	set = 0;
48162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
48262306a36Sopenharmony_ci		if (key.specified & spec_order[i])
48362306a36Sopenharmony_ci			set++;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	if (!set || set > 1) {
48662306a36Sopenharmony_ci		pr_err("SELinux:  avtab:  more than one specifier\n");
48762306a36Sopenharmony_ci		return -EINVAL;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
49162306a36Sopenharmony_ci			(key.specified & AVTAB_XPERMS)) {
49262306a36Sopenharmony_ci		pr_err("SELinux:  avtab:  policy version %u does not "
49362306a36Sopenharmony_ci				"support extended permissions rules and one "
49462306a36Sopenharmony_ci				"was specified\n", vers);
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci	} else if (key.specified & AVTAB_XPERMS) {
49762306a36Sopenharmony_ci		memset(&xperms, 0, sizeof(struct avtab_extended_perms));
49862306a36Sopenharmony_ci		rc = next_entry(&xperms.specified, fp, sizeof(u8));
49962306a36Sopenharmony_ci		if (rc) {
50062306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated entry\n");
50162306a36Sopenharmony_ci			return rc;
50262306a36Sopenharmony_ci		}
50362306a36Sopenharmony_ci		rc = next_entry(&xperms.driver, fp, sizeof(u8));
50462306a36Sopenharmony_ci		if (rc) {
50562306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated entry\n");
50662306a36Sopenharmony_ci			return rc;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p));
50962306a36Sopenharmony_ci		if (rc) {
51062306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated entry\n");
51162306a36Sopenharmony_ci			return rc;
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++)
51462306a36Sopenharmony_ci			xperms.perms.p[i] = le32_to_cpu(buf32[i]);
51562306a36Sopenharmony_ci		datum.u.xperms = &xperms;
51662306a36Sopenharmony_ci	} else {
51762306a36Sopenharmony_ci		rc = next_entry(buf32, fp, sizeof(u32));
51862306a36Sopenharmony_ci		if (rc) {
51962306a36Sopenharmony_ci			pr_err("SELinux: avtab: truncated entry\n");
52062306a36Sopenharmony_ci			return rc;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci		datum.u.data = le32_to_cpu(*buf32);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	if ((key.specified & AVTAB_TYPE) &&
52562306a36Sopenharmony_ci	    !policydb_type_isvalid(pol, datum.u.data)) {
52662306a36Sopenharmony_ci		pr_err("SELinux: avtab: invalid type\n");
52762306a36Sopenharmony_ci		return -EINVAL;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	return insertf(a, &key, &datum, p);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int avtab_insertf(struct avtab *a, const struct avtab_key *k,
53362306a36Sopenharmony_ci			 const struct avtab_datum *d, void *p)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	return avtab_insert(a, k, d);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ciint avtab_read(struct avtab *a, void *fp, struct policydb *pol)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	int rc;
54162306a36Sopenharmony_ci	__le32 buf[1];
54262306a36Sopenharmony_ci	u32 nel, i;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	rc = next_entry(buf, fp, sizeof(u32));
54662306a36Sopenharmony_ci	if (rc < 0) {
54762306a36Sopenharmony_ci		pr_err("SELinux: avtab: truncated table\n");
54862306a36Sopenharmony_ci		goto bad;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci	nel = le32_to_cpu(buf[0]);
55162306a36Sopenharmony_ci	if (!nel) {
55262306a36Sopenharmony_ci		pr_err("SELinux: avtab: table is empty\n");
55362306a36Sopenharmony_ci		rc = -EINVAL;
55462306a36Sopenharmony_ci		goto bad;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	rc = avtab_alloc(a, nel);
55862306a36Sopenharmony_ci	if (rc)
55962306a36Sopenharmony_ci		goto bad;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	for (i = 0; i < nel; i++) {
56262306a36Sopenharmony_ci		rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
56362306a36Sopenharmony_ci		if (rc) {
56462306a36Sopenharmony_ci			if (rc == -ENOMEM)
56562306a36Sopenharmony_ci				pr_err("SELinux: avtab: out of memory\n");
56662306a36Sopenharmony_ci			else if (rc == -EEXIST)
56762306a36Sopenharmony_ci				pr_err("SELinux: avtab: duplicate entry\n");
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci			goto bad;
57062306a36Sopenharmony_ci		}
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	rc = 0;
57462306a36Sopenharmony_ciout:
57562306a36Sopenharmony_ci	return rc;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cibad:
57862306a36Sopenharmony_ci	avtab_destroy(a);
57962306a36Sopenharmony_ci	goto out;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ciint avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	__le16 buf16[4];
58562306a36Sopenharmony_ci	__le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
58662306a36Sopenharmony_ci	int rc;
58762306a36Sopenharmony_ci	unsigned int i;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	buf16[0] = cpu_to_le16(cur->key.source_type);
59062306a36Sopenharmony_ci	buf16[1] = cpu_to_le16(cur->key.target_type);
59162306a36Sopenharmony_ci	buf16[2] = cpu_to_le16(cur->key.target_class);
59262306a36Sopenharmony_ci	buf16[3] = cpu_to_le16(cur->key.specified);
59362306a36Sopenharmony_ci	rc = put_entry(buf16, sizeof(u16), 4, fp);
59462306a36Sopenharmony_ci	if (rc)
59562306a36Sopenharmony_ci		return rc;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (cur->key.specified & AVTAB_XPERMS) {
59862306a36Sopenharmony_ci		rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp);
59962306a36Sopenharmony_ci		if (rc)
60062306a36Sopenharmony_ci			return rc;
60162306a36Sopenharmony_ci		rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp);
60262306a36Sopenharmony_ci		if (rc)
60362306a36Sopenharmony_ci			return rc;
60462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++)
60562306a36Sopenharmony_ci			buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
60662306a36Sopenharmony_ci		rc = put_entry(buf32, sizeof(u32),
60762306a36Sopenharmony_ci				ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
60862306a36Sopenharmony_ci	} else {
60962306a36Sopenharmony_ci		buf32[0] = cpu_to_le32(cur->datum.u.data);
61062306a36Sopenharmony_ci		rc = put_entry(buf32, sizeof(u32), 1, fp);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci	if (rc)
61362306a36Sopenharmony_ci		return rc;
61462306a36Sopenharmony_ci	return 0;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ciint avtab_write(struct policydb *p, struct avtab *a, void *fp)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	u32 i;
62062306a36Sopenharmony_ci	int rc = 0;
62162306a36Sopenharmony_ci	struct avtab_node *cur;
62262306a36Sopenharmony_ci	__le32 buf[1];
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	buf[0] = cpu_to_le32(a->nel);
62562306a36Sopenharmony_ci	rc = put_entry(buf, sizeof(u32), 1, fp);
62662306a36Sopenharmony_ci	if (rc)
62762306a36Sopenharmony_ci		return rc;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	for (i = 0; i < a->nslot; i++) {
63062306a36Sopenharmony_ci		for (cur = a->htable[i]; cur;
63162306a36Sopenharmony_ci		     cur = cur->next) {
63262306a36Sopenharmony_ci			rc = avtab_write_item(p, cur, fp);
63362306a36Sopenharmony_ci			if (rc)
63462306a36Sopenharmony_ci				return rc;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return rc;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_civoid __init avtab_cache_init(void)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	avtab_node_cachep = kmem_cache_create("avtab_node",
64462306a36Sopenharmony_ci					      sizeof(struct avtab_node),
64562306a36Sopenharmony_ci					      0, SLAB_PANIC, NULL);
64662306a36Sopenharmony_ci	avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
64762306a36Sopenharmony_ci						sizeof(struct avtab_extended_perms),
64862306a36Sopenharmony_ci						0, SLAB_PANIC, NULL);
64962306a36Sopenharmony_ci}
650