avtab.c 12.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Implementation of the access vector table type.
 *
 * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
 */

/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
 *
 * 	Added conditional policy language extensions
 *
 * Copyright (C) 2003 Tresys Technology, LLC
 *	This program is free software; you can redistribute it and/or modify
 *  	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, version 2.
15 16 17
 *
 * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
 * 	Tuned number of hash slots for avtab to reduce memory usage
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include "avtab.h"
#include "policydb.h"

26
static struct kmem_cache *avtab_node_cachep;
L
Linus Torvalds 已提交
27

28 29 30 31 32 33
static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
{
	return ((keyp->target_class + (keyp->target_type << 2) +
		 (keyp->source_type << 9)) & mask);
}

L
Linus Torvalds 已提交
34 35 36 37 38 39
static struct avtab_node*
avtab_insert_node(struct avtab *h, int hvalue,
		  struct avtab_node * prev, struct avtab_node * cur,
		  struct avtab_key *key, struct avtab_datum *datum)
{
	struct avtab_node * newnode;
40
	newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
	if (newnode == NULL)
		return NULL;
	newnode->key = *key;
	newnode->datum = *datum;
	if (prev) {
		newnode->next = prev->next;
		prev->next = newnode;
	} else {
		newnode->next = h->htable[hvalue];
		h->htable[hvalue] = newnode;
	}

	h->nel++;
	return newnode;
}

static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
{
	int hvalue;
	struct avtab_node *prev, *cur, *newnode;
61
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
L
Linus Torvalds 已提交
62

63
	if (!h || !h->htable)
L
Linus Torvalds 已提交
64 65
		return -EINVAL;

66
	hvalue = avtab_hash(key, h->mask);
L
Linus Torvalds 已提交
67 68 69 70 71 72
	for (prev = NULL, cur = h->htable[hvalue];
	     cur;
	     prev = cur, cur = cur->next) {
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
73
		    (specified & cur->key.specified))
L
Linus Torvalds 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
			return -EEXIST;
		if (key->source_type < cur->key.source_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type < cur->key.target_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class < cur->key.target_class)
			break;
	}

	newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
	if(!newnode)
		return -ENOMEM;

	return 0;
}

/* Unlike avtab_insert(), this function allow multiple insertions of the same
 * key/specified mask into the table, as needed by the conditional avtab.
 * It also returns a pointer to the node inserted.
 */
struct avtab_node *
avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum)
{
	int hvalue;
	struct avtab_node *prev, *cur, *newnode;
102
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
L
Linus Torvalds 已提交
103

104
	if (!h || !h->htable)
L
Linus Torvalds 已提交
105
		return NULL;
106
	hvalue = avtab_hash(key, h->mask);
L
Linus Torvalds 已提交
107 108 109 110 111 112
	for (prev = NULL, cur = h->htable[hvalue];
	     cur;
	     prev = cur, cur = cur->next) {
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
113
		    (specified & cur->key.specified))
L
Linus Torvalds 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
			break;
		if (key->source_type < cur->key.source_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type < cur->key.target_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class < cur->key.target_class)
			break;
	}
	newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);

	return newnode;
}

130
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
L
Linus Torvalds 已提交
131 132 133
{
	int hvalue;
	struct avtab_node *cur;
134
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
L
Linus Torvalds 已提交
135

136
	if (!h || !h->htable)
L
Linus Torvalds 已提交
137 138
		return NULL;

139
	hvalue = avtab_hash(key, h->mask);
L
Linus Torvalds 已提交
140 141 142 143
	for (cur = h->htable[hvalue]; cur; cur = cur->next) {
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
144
		    (specified & cur->key.specified))
L
Linus Torvalds 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
			return &cur->datum;

		if (key->source_type < cur->key.source_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type < cur->key.target_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class < cur->key.target_class)
			break;
	}

	return NULL;
}

/* This search function returns a node pointer, and can be used in
 * conjunction with avtab_search_next_node()
 */
struct avtab_node*
165
avtab_search_node(struct avtab *h, struct avtab_key *key)
L
Linus Torvalds 已提交
166 167 168
{
	int hvalue;
	struct avtab_node *cur;
169
	u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
L
Linus Torvalds 已提交
170

171
	if (!h || !h->htable)
L
Linus Torvalds 已提交
172 173
		return NULL;

174
	hvalue = avtab_hash(key, h->mask);
L
Linus Torvalds 已提交
175 176 177 178
	for (cur = h->htable[hvalue]; cur; cur = cur->next) {
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
179
		    (specified & cur->key.specified))
L
Linus Torvalds 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
			return cur;

		if (key->source_type < cur->key.source_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type < cur->key.target_type)
			break;
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class < cur->key.target_class)
			break;
	}
	return NULL;
}

struct avtab_node*
avtab_search_node_next(struct avtab_node *node, int specified)
{
	struct avtab_node *cur;

	if (!node)
		return NULL;

203
	specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
L
Linus Torvalds 已提交
204 205 206 207
	for (cur = node->next; cur; cur = cur->next) {
		if (node->key.source_type == cur->key.source_type &&
		    node->key.target_type == cur->key.target_type &&
		    node->key.target_class == cur->key.target_class &&
208
		    (specified & cur->key.specified))
L
Linus Torvalds 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
			return cur;

		if (node->key.source_type < cur->key.source_type)
			break;
		if (node->key.source_type == cur->key.source_type &&
		    node->key.target_type < cur->key.target_type)
			break;
		if (node->key.source_type == cur->key.source_type &&
		    node->key.target_type == cur->key.target_type &&
		    node->key.target_class < cur->key.target_class)
			break;
	}
	return NULL;
}

void avtab_destroy(struct avtab *h)
{
	int i;
	struct avtab_node *cur, *temp;

	if (!h || !h->htable)
		return;

232
	for (i = 0; i < h->nslot; i++) {
L
Linus Torvalds 已提交
233 234 235 236 237 238 239 240
		cur = h->htable[i];
		while (cur != NULL) {
			temp = cur;
			cur = cur->next;
			kmem_cache_free(avtab_node_cachep, temp);
		}
		h->htable[i] = NULL;
	}
241
	kfree(h->htable);
L
Linus Torvalds 已提交
242
	h->htable = NULL;
243 244
	h->nslot = 0;
	h->mask = 0;
L
Linus Torvalds 已提交
245 246 247 248
}

int avtab_init(struct avtab *h)
{
249 250 251 252 253 254 255 256 257 258 259 260 261 262
	h->htable = NULL;
	h->nel = 0;
	return 0;
}

int avtab_alloc(struct avtab *h, u32 nrules)
{
	u16 mask = 0;
	u32 shift = 0;
	u32 work = nrules;
	u32 nslot = 0;

	if (nrules == 0)
		goto avtab_alloc_out;
L
Linus Torvalds 已提交
263

264 265 266 267 268 269 270 271 272 273 274 275
	while (work) {
		work  = work >> 1;
		shift++;
	}
	if (shift > 2)
		shift = shift - 2;
	nslot = 1 << shift;
	if (nslot > MAX_AVTAB_SIZE)
		nslot = MAX_AVTAB_SIZE;
	mask = nslot - 1;

	h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL);
L
Linus Torvalds 已提交
276 277
	if (!h->htable)
		return -ENOMEM;
278 279

 avtab_alloc_out:
L
Linus Torvalds 已提交
280
	h->nel = 0;
281 282
	h->nslot = nslot;
	h->mask = mask;
J
James Morris 已提交
283 284
	printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n",
	       h->nslot, nrules);
L
Linus Torvalds 已提交
285 286 287 288 289 290
	return 0;
}

void avtab_hash_eval(struct avtab *h, char *tag)
{
	int i, chain_len, slots_used, max_chain_len;
291
	unsigned long long chain2_len_sum;
L
Linus Torvalds 已提交
292 293 294 295
	struct avtab_node *cur;

	slots_used = 0;
	max_chain_len = 0;
296 297
	chain2_len_sum = 0;
	for (i = 0; i < h->nslot; i++) {
L
Linus Torvalds 已提交
298 299 300 301 302 303 304 305 306 307 308
		cur = h->htable[i];
		if (cur) {
			slots_used++;
			chain_len = 0;
			while (cur) {
				chain_len++;
				cur = cur->next;
			}

			if (chain_len > max_chain_len)
				max_chain_len = chain_len;
309
			chain2_len_sum += chain_len * chain_len;
L
Linus Torvalds 已提交
310 311 312
		}
	}

313
	printk(KERN_DEBUG "%s:  %d entries and %d/%d buckets used, longest "
314 315 316
	       "chain length %d sum of chain length^2 %Lu\n",
	       tag, h->nel, slots_used, h->nslot, max_chain_len,
	       chain2_len_sum);
L
Linus Torvalds 已提交
317 318
}

319 320 321 322 323 324 325 326 327
static uint16_t spec_order[] = {
	AVTAB_ALLOWED,
	AVTAB_AUDITDENY,
	AVTAB_AUDITALLOW,
	AVTAB_TRANSITION,
	AVTAB_CHANGE,
	AVTAB_MEMBER
};

328
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
329 330 331
	            int (*insertf)(struct avtab *a, struct avtab_key *k,
				   struct avtab_datum *d, void *p),
		    void *p)
L
Linus Torvalds 已提交
332
{
333 334 335
	__le16 buf16[4];
	u16 enabled;
	__le32 buf32[7];
336
	u32 items, items2, val, vers = pol->policyvers;
337 338 339
	struct avtab_key key;
	struct avtab_datum datum;
	int i, rc;
340
	unsigned set;
341 342 343 344 345 346 347

	memset(&key, 0, sizeof(struct avtab_key));
	memset(&datum, 0, sizeof(struct avtab_datum));

	if (vers < POLICYDB_VERSION_AVTAB) {
		rc = next_entry(buf32, fp, sizeof(u32));
		if (rc < 0) {
J
James Morris 已提交
348
			printk(KERN_ERR "SELinux: avtab: truncated entry\n");
349 350 351 352
			return -1;
		}
		items2 = le32_to_cpu(buf32[0]);
		if (items2 > ARRAY_SIZE(buf32)) {
J
James Morris 已提交
353
			printk(KERN_ERR "SELinux: avtab: entry overflow\n");
354
			return -1;
L
Linus Torvalds 已提交
355

356 357 358
		}
		rc = next_entry(buf32, fp, sizeof(u32)*items2);
		if (rc < 0) {
J
James Morris 已提交
359
			printk(KERN_ERR "SELinux: avtab: truncated entry\n");
360 361 362
			return -1;
		}
		items = 0;
L
Linus Torvalds 已提交
363

364 365 366
		val = le32_to_cpu(buf32[items++]);
		key.source_type = (u16)val;
		if (key.source_type != val) {
J
James Morris 已提交
367
			printk("SELinux: avtab: truncated source type\n");
368 369 370 371 372
			return -1;
		}
		val = le32_to_cpu(buf32[items++]);
		key.target_type = (u16)val;
		if (key.target_type != val) {
J
James Morris 已提交
373
			printk("SELinux: avtab: truncated target type\n");
374 375 376 377 378
			return -1;
		}
		val = le32_to_cpu(buf32[items++]);
		key.target_class = (u16)val;
		if (key.target_class != val) {
J
James Morris 已提交
379
			printk("SELinux: avtab: truncated target class\n");
380 381 382 383 384 385 386
			return -1;
		}

		val = le32_to_cpu(buf32[items++]);
		enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;

		if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
J
James Morris 已提交
387
			printk("SELinux: avtab: null entry\n");
388 389 390 391
			return -1;
		}
		if ((val & AVTAB_AV) &&
		    (val & AVTAB_TYPE)) {
J
James Morris 已提交
392
			printk("SELinux: avtab: entry has both access vectors and types\n");
393 394 395
			return -1;
		}

396
		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
397 398 399 400 401 402 403 404 405
			if (val & spec_order[i]) {
				key.specified = spec_order[i] | enabled;
				datum.data = le32_to_cpu(buf32[items++]);
				rc = insertf(a, &key, &datum, p);
				if (rc) return rc;
			}
		}

		if (items != items2) {
J
James Morris 已提交
406
			printk("SELinux: avtab: entry only had %d items, expected %d\n", items2, items);
407 408 409
			return -1;
		}
		return 0;
L
Linus Torvalds 已提交
410
	}
411 412

	rc = next_entry(buf16, fp, sizeof(u16)*4);
L
Linus Torvalds 已提交
413
	if (rc < 0) {
J
James Morris 已提交
414
		printk("SELinux: avtab: truncated entry\n");
415
		return -1;
L
Linus Torvalds 已提交
416
	}
417

L
Linus Torvalds 已提交
418
	items = 0;
419 420 421 422 423
	key.source_type = le16_to_cpu(buf16[items++]);
	key.target_type = le16_to_cpu(buf16[items++]);
	key.target_class = le16_to_cpu(buf16[items++]);
	key.specified = le16_to_cpu(buf16[items++]);

424 425 426
	if (!policydb_type_isvalid(pol, key.source_type) ||
	    !policydb_type_isvalid(pol, key.target_type) ||
	    !policydb_class_isvalid(pol, key.target_class)) {
J
James Morris 已提交
427
		printk(KERN_WARNING "SELinux: avtab: invalid type or class\n");
428 429 430 431 432 433 434 435 436 437
		return -1;
	}

	set = 0;
	for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
		if (key.specified & spec_order[i])
			set++;
	}
	if (!set || set > 1) {
		printk(KERN_WARNING
J
James Morris 已提交
438
			"SELinux:  avtab:  more than one specifier\n");
439 440 441
		return -1;
	}

442 443
	rc = next_entry(buf32, fp, sizeof(u32));
	if (rc < 0) {
J
James Morris 已提交
444
		printk("SELinux: avtab: truncated entry\n");
445
		return -1;
L
Linus Torvalds 已提交
446
	}
447
	datum.data = le32_to_cpu(*buf32);
448 449
	if ((key.specified & AVTAB_TYPE) &&
	    !policydb_type_isvalid(pol, datum.data)) {
J
James Morris 已提交
450
		printk(KERN_WARNING "SELinux: avtab: invalid type\n");
451 452
		return -1;
	}
453 454
	return insertf(a, &key, &datum, p);
}
L
Linus Torvalds 已提交
455

456 457 458 459
static int avtab_insertf(struct avtab *a, struct avtab_key *k,
			 struct avtab_datum *d, void *p)
{
	return avtab_insert(a, k, d);
L
Linus Torvalds 已提交
460 461
}

462
int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
L
Linus Torvalds 已提交
463 464
{
	int rc;
465
	__le32 buf[1];
L
Linus Torvalds 已提交
466 467 468 469 470
	u32 nel, i;


	rc = next_entry(buf, fp, sizeof(u32));
	if (rc < 0) {
J
James Morris 已提交
471
		printk(KERN_ERR "SELinux: avtab: truncated table\n");
L
Linus Torvalds 已提交
472 473 474 475
		goto bad;
	}
	nel = le32_to_cpu(buf[0]);
	if (!nel) {
J
James Morris 已提交
476
		printk(KERN_ERR "SELinux: avtab: table is empty\n");
L
Linus Torvalds 已提交
477 478 479
		rc = -EINVAL;
		goto bad;
	}
480 481 482 483 484

	rc = avtab_alloc(a, nel);
	if (rc)
		goto bad;

L
Linus Torvalds 已提交
485
	for (i = 0; i < nel; i++) {
486
		rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
L
Linus Torvalds 已提交
487 488
		if (rc) {
			if (rc == -ENOMEM)
J
James Morris 已提交
489
				printk(KERN_ERR "SELinux: avtab: out of memory\n");
490
			else if (rc == -EEXIST)
J
James Morris 已提交
491
				printk(KERN_ERR "SELinux: avtab: duplicate entry\n");
492 493
			else
				rc = -EINVAL;
L
Linus Torvalds 已提交
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
			goto bad;
		}
	}

	rc = 0;
out:
	return rc;

bad:
	avtab_destroy(a);
	goto out;
}

void avtab_cache_init(void)
{
	avtab_node_cachep = kmem_cache_create("avtab_node",
					      sizeof(struct avtab_node),
511
					      0, SLAB_PANIC, NULL);
L
Linus Torvalds 已提交
512 513 514 515 516 517
}

void avtab_cache_destroy(void)
{
	kmem_cache_destroy (avtab_node_cachep);
}