avc.c 20.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
/*
 * Implementation of the kernel access vector cache (AVC).
 *
 * Authors:  Stephen Smalley, <sds@epoch.ncsc.mil>
5
 *	     James Morris <jmorris@redhat.com>
Linus Torvalds's avatar
Linus Torvalds committed
6
7
 *
 * Update:   KaiGai, Kohei <kaigai@ak.jp.nec.com>
8
 *	Replaced the avc_lock spinlock by RCU.
Linus Torvalds's avatar
Linus Torvalds committed
9
10
11
12
13
 *
 * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License version 2,
14
 *	as published by the Free Software Foundation.
Linus Torvalds's avatar
Linus Torvalds committed
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 */
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/percpu.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
#include <linux/ip.h>
#include <linux/audit.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include "avc.h"
#include "avc_ss.h"
34
#include "classmap.h"
35

Linus Torvalds's avatar
Linus Torvalds committed
36
37
38
39
40
#define AVC_CACHE_SLOTS			512
#define AVC_DEF_CACHE_THRESHOLD		512
#define AVC_CACHE_RECLAIM		16

#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
41
#define avc_cache_stats_incr(field)				\
Linus Torvalds's avatar
Linus Torvalds committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
do {								\
	per_cpu(avc_cache_stats, get_cpu()).field++;		\
	put_cpu();						\
} while (0)
#else
#define avc_cache_stats_incr(field)	do {} while (0)
#endif

struct avc_entry {
	u32			ssid;
	u32			tsid;
	u16			tclass;
	struct av_decision	avd;
};

struct avc_node {
	struct avc_entry	ae;
59
	struct hlist_node	list; /* anchored in avc_cache->slots[i] */
60
	struct rcu_head		rhead;
Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
};

struct avc_cache {
64
	struct hlist_head	slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
Linus Torvalds's avatar
Linus Torvalds committed
65
66
67
68
69
70
71
72
	spinlock_t		slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
	atomic_t		lru_hint;	/* LRU hint for reclaim scan */
	atomic_t		active_nodes;
	u32			latest_notif;	/* latest revocation notification */
};

struct avc_callback_node {
	int (*callback) (u32 event, u32 ssid, u32 tsid,
73
74
			 u16 tclass, u32 perms,
			 u32 *out_retained);
Linus Torvalds's avatar
Linus Torvalds committed
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
	u32 events;
	u32 ssid;
	u32 tsid;
	u16 tclass;
	u32 perms;
	struct avc_callback_node *next;
};

/* Exported via selinufs */
unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;

#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
#endif

static struct avc_cache avc_cache;
static struct avc_callback_node *avc_callbacks;
92
static struct kmem_cache *avc_node_cachep;
Linus Torvalds's avatar
Linus Torvalds committed
93
94
95
96
97
98
99
100
101
102
103

static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
	return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
}

/**
 * avc_dump_av - Display an access vector in human-readable form.
 * @tclass: target security class
 * @av: access vector
 */
104
static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
Linus Torvalds's avatar
Linus Torvalds committed
105
{
106
107
	const char **perms;
	int i, perm;
Linus Torvalds's avatar
Linus Torvalds committed
108
109
110
111
112
113

	if (av == 0) {
		audit_log_format(ab, " null");
		return;
	}

114
	perms = secclass_map[tclass-1].perms;
Linus Torvalds's avatar
Linus Torvalds committed
115
116
117
118

	audit_log_format(ab, " {");
	i = 0;
	perm = 1;
119
	while (i < (sizeof(av) * 8)) {
120
		if ((perm & av) && perms[i]) {
121
			audit_log_format(ab, " %s", perms[i]);
Linus Torvalds's avatar
Linus Torvalds committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
			av &= ~perm;
		}
		i++;
		perm <<= 1;
	}

	if (av)
		audit_log_format(ab, " 0x%x", av);

	audit_log_format(ab, " }");
}

/**
 * avc_dump_query - Display a SID pair and a class in human-readable form.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 */
static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
{
	int rc;
	char *scontext;
	u32 scontext_len;

146
	rc = security_sid_to_context(ssid, &scontext, &scontext_len);
Linus Torvalds's avatar
Linus Torvalds committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
	if (rc)
		audit_log_format(ab, "ssid=%d", ssid);
	else {
		audit_log_format(ab, "scontext=%s", scontext);
		kfree(scontext);
	}

	rc = security_sid_to_context(tsid, &scontext, &scontext_len);
	if (rc)
		audit_log_format(ab, " tsid=%d", tsid);
	else {
		audit_log_format(ab, " tcontext=%s", scontext);
		kfree(scontext);
	}
161

162
163
	BUG_ON(tclass >= ARRAY_SIZE(secclass_map));
	audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name);
Linus Torvalds's avatar
Linus Torvalds committed
164
165
166
167
168
169
170
171
172
173
174
175
}

/**
 * avc_init - Initialize the AVC.
 *
 * Initialize the access vector cache.
 */
void __init avc_init(void)
{
	int i;

	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
176
		INIT_HLIST_HEAD(&avc_cache.slots[i]);
Linus Torvalds's avatar
Linus Torvalds committed
177
178
179
180
181
182
		spin_lock_init(&avc_cache.slots_lock[i]);
	}
	atomic_set(&avc_cache.active_nodes, 0);
	atomic_set(&avc_cache.lru_hint, 0);

	avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
183
					     0, SLAB_PANIC, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
184

185
	audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
Linus Torvalds's avatar
Linus Torvalds committed
186
187
188
189
190
191
}

int avc_get_hash_stats(char *page)
{
	int i, chain_len, max_chain_len, slots_used;
	struct avc_node *node;
192
	struct hlist_head *head;
Linus Torvalds's avatar
Linus Torvalds committed
193
194
195
196
197
198

	rcu_read_lock();

	slots_used = 0;
	max_chain_len = 0;
	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
199
		head = &avc_cache.slots[i];
200
201
202
		if (!hlist_empty(head)) {
			struct hlist_node *next;

Linus Torvalds's avatar
Linus Torvalds committed
203
204
			slots_used++;
			chain_len = 0;
205
			hlist_for_each_entry_rcu(node, next, head, list)
Linus Torvalds's avatar
Linus Torvalds committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
				chain_len++;
			if (chain_len > max_chain_len)
				max_chain_len = chain_len;
		}
	}

	rcu_read_unlock();

	return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
			 "longest chain: %d\n",
			 atomic_read(&avc_cache.active_nodes),
			 slots_used, AVC_CACHE_SLOTS, max_chain_len);
}

static void avc_node_free(struct rcu_head *rhead)
{
	struct avc_node *node = container_of(rhead, struct avc_node, rhead);
	kmem_cache_free(avc_node_cachep, node);
	avc_cache_stats_incr(frees);
}

static void avc_node_delete(struct avc_node *node)
{
229
	hlist_del_rcu(&node->list);
Linus Torvalds's avatar
Linus Torvalds committed
230
231
232
233
234
235
236
237
238
239
240
241
242
	call_rcu(&node->rhead, avc_node_free);
	atomic_dec(&avc_cache.active_nodes);
}

static void avc_node_kill(struct avc_node *node)
{
	kmem_cache_free(avc_node_cachep, node);
	avc_cache_stats_incr(frees);
	atomic_dec(&avc_cache.active_nodes);
}

static void avc_node_replace(struct avc_node *new, struct avc_node *old)
{
243
	hlist_replace_rcu(&old->list, &new->list);
Linus Torvalds's avatar
Linus Torvalds committed
244
245
246
247
248
249
250
251
252
	call_rcu(&old->rhead, avc_node_free);
	atomic_dec(&avc_cache.active_nodes);
}

static inline int avc_reclaim_node(void)
{
	struct avc_node *node;
	int hvalue, try, ecx;
	unsigned long flags;
253
254
	struct hlist_head *head;
	struct hlist_node *next;
255
	spinlock_t *lock;
Linus Torvalds's avatar
Linus Torvalds committed
256

257
	for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
Linus Torvalds's avatar
Linus Torvalds committed
258
		hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
259
260
		head = &avc_cache.slots[hvalue];
		lock = &avc_cache.slots_lock[hvalue];
Linus Torvalds's avatar
Linus Torvalds committed
261

262
		if (!spin_trylock_irqsave(lock, flags))
Linus Torvalds's avatar
Linus Torvalds committed
263
264
			continue;

265
		rcu_read_lock();
266
		hlist_for_each_entry(node, next, head, list) {
267
268
269
270
271
			avc_node_delete(node);
			avc_cache_stats_incr(reclaims);
			ecx++;
			if (ecx >= AVC_CACHE_RECLAIM) {
				rcu_read_unlock();
272
				spin_unlock_irqrestore(lock, flags);
273
				goto out;
Linus Torvalds's avatar
Linus Torvalds committed
274
275
			}
		}
276
		rcu_read_unlock();
277
		spin_unlock_irqrestore(lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
278
279
280
281
282
283
284
285
286
	}
out:
	return ecx;
}

static struct avc_node *avc_alloc_node(void)
{
	struct avc_node *node;

287
	node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
288
289
290
	if (!node)
		goto out;

291
	INIT_HLIST_NODE(&node->list);
Linus Torvalds's avatar
Linus Torvalds committed
292
293
294
295
296
297
298
299
300
	avc_cache_stats_incr(allocations);

	if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
		avc_reclaim_node();

out:
	return node;
}

301
static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
Linus Torvalds's avatar
Linus Torvalds committed
302
303
304
305
{
	node->ae.ssid = ssid;
	node->ae.tsid = tsid;
	node->ae.tclass = tclass;
306
	memcpy(&node->ae.avd, avd, sizeof(node->ae.avd));
Linus Torvalds's avatar
Linus Torvalds committed
307
308
309
310
311
312
}

static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
{
	struct avc_node *node, *ret = NULL;
	int hvalue;
313
314
	struct hlist_head *head;
	struct hlist_node *next;
Linus Torvalds's avatar
Linus Torvalds committed
315
316

	hvalue = avc_hash(ssid, tsid, tclass);
317
	head = &avc_cache.slots[hvalue];
318
	hlist_for_each_entry_rcu(node, next, head, list) {
Linus Torvalds's avatar
Linus Torvalds committed
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
		if (ssid == node->ae.ssid &&
		    tclass == node->ae.tclass &&
		    tsid == node->ae.tsid) {
			ret = node;
			break;
		}
	}

	return ret;
}

/**
 * avc_lookup - Look up an AVC entry.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 *
 * Look up an AVC entry that is valid for the
 * (@ssid, @tsid), interpreting the permissions
 * based on @tclass.  If a valid AVC entry exists,
Justin P. Mattock's avatar
Justin P. Mattock committed
339
 * then this function returns the avc_node.
Linus Torvalds's avatar
Linus Torvalds committed
340
341
 * Otherwise, this function returns NULL.
 */
342
static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
Linus Torvalds's avatar
Linus Torvalds committed
343
344
345
346
347
348
{
	struct avc_node *node;

	avc_cache_stats_incr(lookups);
	node = avc_search_node(ssid, tsid, tclass);

349
	if (node)
Linus Torvalds's avatar
Linus Torvalds committed
350
		avc_cache_stats_incr(hits);
351
352
	else
		avc_cache_stats_incr(misses);
Linus Torvalds's avatar
Linus Torvalds committed
353
354
355
356
357
358
359
360
361
362
363
364
365

	return node;
}

static int avc_latest_notif_update(int seqno, int is_insert)
{
	int ret = 0;
	static DEFINE_SPINLOCK(notif_lock);
	unsigned long flag;

	spin_lock_irqsave(&notif_lock, flag);
	if (is_insert) {
		if (seqno < avc_cache.latest_notif) {
Eric Paris's avatar
Eric Paris committed
366
			printk(KERN_WARNING "SELinux: avc:  seqno %d < latest_notif %d\n",
Linus Torvalds's avatar
Linus Torvalds committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
			       seqno, avc_cache.latest_notif);
			ret = -EAGAIN;
		}
	} else {
		if (seqno > avc_cache.latest_notif)
			avc_cache.latest_notif = seqno;
	}
	spin_unlock_irqrestore(&notif_lock, flag);

	return ret;
}

/**
 * avc_insert - Insert an AVC entry.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
384
 * @avd: resulting av decision
Linus Torvalds's avatar
Linus Torvalds committed
385
386
387
388
389
390
 *
 * Insert an AVC entry for the SID pair
 * (@ssid, @tsid) and class @tclass.
 * The access vectors and the sequence number are
 * normally provided by the security server in
 * response to a security_compute_av() call.  If the
391
 * sequence number @avd->seqno is not less than the latest
Linus Torvalds's avatar
Linus Torvalds committed
392
393
394
395
 * revocation notification, then the function copies
 * the access vectors into a cache entry, returns
 * avc_node inserted. Otherwise, this function returns NULL.
 */
396
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
Linus Torvalds's avatar
Linus Torvalds committed
397
398
399
400
401
{
	struct avc_node *pos, *node = NULL;
	int hvalue;
	unsigned long flag;

402
	if (avc_latest_notif_update(avd->seqno, 1))
Linus Torvalds's avatar
Linus Torvalds committed
403
404
405
406
		goto out;

	node = avc_alloc_node();
	if (node) {
407
408
		struct hlist_head *head;
		struct hlist_node *next;
409
410
		spinlock_t *lock;

Linus Torvalds's avatar
Linus Torvalds committed
411
		hvalue = avc_hash(ssid, tsid, tclass);
412
		avc_node_populate(node, ssid, tsid, tclass, avd);
Linus Torvalds's avatar
Linus Torvalds committed
413

414
415
416
417
		head = &avc_cache.slots[hvalue];
		lock = &avc_cache.slots_lock[hvalue];

		spin_lock_irqsave(lock, flag);
418
		hlist_for_each_entry(pos, next, head, list) {
Linus Torvalds's avatar
Linus Torvalds committed
419
420
421
			if (pos->ae.ssid == ssid &&
			    pos->ae.tsid == tsid &&
			    pos->ae.tclass == tclass) {
422
				avc_node_replace(node, pos);
Linus Torvalds's avatar
Linus Torvalds committed
423
424
425
				goto found;
			}
		}
426
		hlist_add_head_rcu(&node->list, head);
Linus Torvalds's avatar
Linus Torvalds committed
427
found:
428
		spin_unlock_irqrestore(lock, flag);
Linus Torvalds's avatar
Linus Torvalds committed
429
430
431
432
433
	}
out:
	return node;
}

434
435
436
437
438
439
440
/**
 * avc_audit_pre_callback - SELinux specific information
 * will be called by generic audit code
 * @ab: the audit buffer
 * @a: audit_data
 */
static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
Linus Torvalds's avatar
Linus Torvalds committed
441
{
442
443
444
445
446
447
	struct common_audit_data *ad = a;
	audit_log_format(ab, "avc:  %s ",
			 ad->selinux_audit_data.denied ? "denied" : "granted");
	avc_dump_av(ab, ad->selinux_audit_data.tclass,
			ad->selinux_audit_data.audited);
	audit_log_format(ab, " for ");
Linus Torvalds's avatar
Linus Torvalds committed
448
449
}

450
451
452
453
454
455
456
/**
 * avc_audit_post_callback - SELinux specific information
 * will be called by generic audit code
 * @ab: the audit buffer
 * @a: audit_data
 */
static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
Linus Torvalds's avatar
Linus Torvalds committed
457
{
458
459
460
461
462
	struct common_audit_data *ad = a;
	audit_log_format(ab, " ");
	avc_dump_query(ab, ad->selinux_audit_data.ssid,
			   ad->selinux_audit_data.tsid,
			   ad->selinux_audit_data.tclass);
Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
}

/**
 * avc_audit - Audit the granting or denial of permissions.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 * @requested: requested permissions
 * @avd: access vector decisions
 * @result: result from avc_has_perm_noaudit
 * @a:  auxiliary audit data
 *
 * Audit the granting or denial of permissions in accordance
 * with the policy.  This function is typically called by
 * avc_has_perm() after a permission check, but can also be
 * called directly by callers who use avc_has_perm_noaudit()
 * in order to separate the permission check from the auditing.
 * For example, this separation is useful when the permission check must
 * be performed under a lock, to allow the lock to be released
 * before calling the auditing code.
 */
void avc_audit(u32 ssid, u32 tsid,
485
	       u16 tclass, u32 requested,
486
	       struct av_decision *avd, int result, struct common_audit_data *a)
Linus Torvalds's avatar
Linus Torvalds committed
487
{
488
	struct common_audit_data stack_data;
489
490
	u32 denied, audited;
	denied = requested & ~avd->allowed;
491
492
493
	if (denied)
		audited = denied & avd->auditdeny;
	else if (result)
494
		audited = denied = requested;
495
496
497
498
	else
		audited = requested & avd->auditallow;
	if (!audited)
		return;
499
500
	if (!a) {
		a = &stack_data;
501
		COMMON_AUDIT_DATA_INIT(a, NONE);
502
	}
503
504
505
506
507
508
509
510
511
	a->selinux_audit_data.tclass = tclass;
	a->selinux_audit_data.requested = requested;
	a->selinux_audit_data.ssid = ssid;
	a->selinux_audit_data.tsid = tsid;
	a->selinux_audit_data.audited = audited;
	a->selinux_audit_data.denied = denied;
	a->lsm_pre_audit = avc_audit_pre_callback;
	a->lsm_post_audit = avc_audit_post_callback;
	common_lsm_audit(a);
Linus Torvalds's avatar
Linus Torvalds committed
512
513
514
515
516
517
518
519
520
521
522
523
}

/**
 * avc_add_callback - Register a callback for security events.
 * @callback: callback function
 * @events: security events
 * @ssid: source security identifier or %SECSID_WILD
 * @tsid: target security identifier or %SECSID_WILD
 * @tclass: target security class
 * @perms: permissions
 *
 * Register a callback function for events in the set @events
Justin P. Mattock's avatar
Justin P. Mattock committed
524
 * related to the SID pair (@ssid, @tsid) 
Linus Torvalds's avatar
Linus Torvalds committed
525
526
527
528
529
 * and the permissions @perms, interpreting
 * @perms based on @tclass.  Returns %0 on success or
 * -%ENOMEM if insufficient memory exists to add the callback.
 */
int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
530
531
532
533
				     u16 tclass, u32 perms,
				     u32 *out_retained),
		     u32 events, u32 ssid, u32 tsid,
		     u16 tclass, u32 perms)
Linus Torvalds's avatar
Linus Torvalds committed
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
{
	struct avc_callback_node *c;
	int rc = 0;

	c = kmalloc(sizeof(*c), GFP_ATOMIC);
	if (!c) {
		rc = -ENOMEM;
		goto out;
	}

	c->callback = callback;
	c->events = events;
	c->ssid = ssid;
	c->tsid = tsid;
	c->perms = perms;
	c->next = avc_callbacks;
	avc_callbacks = c;
out:
	return rc;
}

static inline int avc_sidcmp(u32 x, u32 y)
{
	return (x == y || x == SECSID_WILD || y == SECSID_WILD);
}

/**
 * avc_update_node Update an AVC entry
 * @event : Updating event
 * @perms : Permission mask bits
 * @ssid,@tsid,@tclass : identifier of an AVC entry
565
 * @seqno : sequence number when decision was made
Linus Torvalds's avatar
Linus Torvalds committed
566
567
568
 *
 * if a valid AVC entry doesn't exist,this function returns -ENOENT.
 * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
Justin P. Mattock's avatar
Justin P. Mattock committed
569
 * otherwise, this function updates the AVC entry. The original AVC-entry object
Linus Torvalds's avatar
Linus Torvalds committed
570
571
 * will release later by RCU.
 */
572
573
static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
			   u32 seqno)
Linus Torvalds's avatar
Linus Torvalds committed
574
575
576
577
{
	int hvalue, rc = 0;
	unsigned long flag;
	struct avc_node *pos, *node, *orig = NULL;
578
579
	struct hlist_head *head;
	struct hlist_node *next;
580
	spinlock_t *lock;
Linus Torvalds's avatar
Linus Torvalds committed
581
582
583
584
585
586
587
588
589
590

	node = avc_alloc_node();
	if (!node) {
		rc = -ENOMEM;
		goto out;
	}

	/* Lock the target slot */
	hvalue = avc_hash(ssid, tsid, tclass);

591
592
593
594
595
	head = &avc_cache.slots[hvalue];
	lock = &avc_cache.slots_lock[hvalue];

	spin_lock_irqsave(lock, flag);

596
	hlist_for_each_entry(pos, next, head, list) {
597
598
		if (ssid == pos->ae.ssid &&
		    tsid == pos->ae.tsid &&
599
600
		    tclass == pos->ae.tclass &&
		    seqno == pos->ae.avd.seqno){
Linus Torvalds's avatar
Linus Torvalds committed
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
			orig = pos;
			break;
		}
	}

	if (!orig) {
		rc = -ENOENT;
		avc_node_kill(node);
		goto out_unlock;
	}

	/*
	 * Copy and replace original node.
	 */

616
	avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
Linus Torvalds's avatar
Linus Torvalds committed
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640

	switch (event) {
	case AVC_CALLBACK_GRANT:
		node->ae.avd.allowed |= perms;
		break;
	case AVC_CALLBACK_TRY_REVOKE:
	case AVC_CALLBACK_REVOKE:
		node->ae.avd.allowed &= ~perms;
		break;
	case AVC_CALLBACK_AUDITALLOW_ENABLE:
		node->ae.avd.auditallow |= perms;
		break;
	case AVC_CALLBACK_AUDITALLOW_DISABLE:
		node->ae.avd.auditallow &= ~perms;
		break;
	case AVC_CALLBACK_AUDITDENY_ENABLE:
		node->ae.avd.auditdeny |= perms;
		break;
	case AVC_CALLBACK_AUDITDENY_DISABLE:
		node->ae.avd.auditdeny &= ~perms;
		break;
	}
	avc_node_replace(node, orig);
out_unlock:
641
	spin_unlock_irqrestore(lock, flag);
Linus Torvalds's avatar
Linus Torvalds committed
642
643
644
645
646
out:
	return rc;
}

/**
647
 * avc_flush - Flush the cache
Linus Torvalds's avatar
Linus Torvalds committed
648
 */
649
static void avc_flush(void)
Linus Torvalds's avatar
Linus Torvalds committed
650
{
651
652
	struct hlist_head *head;
	struct hlist_node *next;
653
	struct avc_node *node;
654
	spinlock_t *lock;
655
656
	unsigned long flag;
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
657
658

	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
659
660
661
662
		head = &avc_cache.slots[i];
		lock = &avc_cache.slots_lock[i];

		spin_lock_irqsave(lock, flag);
663
664
665
666
667
		/*
		 * With preemptable RCU, the outer spinlock does not
		 * prevent RCU grace periods from ending.
		 */
		rcu_read_lock();
668
		hlist_for_each_entry(node, next, head, list)
Linus Torvalds's avatar
Linus Torvalds committed
669
			avc_node_delete(node);
670
		rcu_read_unlock();
671
		spin_unlock_irqrestore(lock, flag);
Linus Torvalds's avatar
Linus Torvalds committed
672
	}
673
674
675
676
677
678
679
680
681
682
683
684
}

/**
 * avc_ss_reset - Flush the cache and revalidate migrated permissions.
 * @seqno: policy sequence number
 */
int avc_ss_reset(u32 seqno)
{
	struct avc_callback_node *c;
	int rc = 0, tmprc;

	avc_flush();
Linus Torvalds's avatar
Linus Torvalds committed
685
686
687

	for (c = avc_callbacks; c; c = c->next) {
		if (c->events & AVC_CALLBACK_RESET) {
688
			tmprc = c->callback(AVC_CALLBACK_RESET,
689
					    0, 0, 0, 0, NULL);
690
691
692
693
			/* save the first error encountered for the return
			   value and continue processing the callbacks */
			if (!rc)
				rc = tmprc;
Linus Torvalds's avatar
Linus Torvalds committed
694
695
696
697
698
699
700
701
702
703
704
705
706
		}
	}

	avc_latest_notif_update(seqno, 0);
	return rc;
}

/**
 * avc_has_perm_noaudit - Check permissions but perform no auditing.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 * @requested: requested permissions, interpreted based on @tclass
707
 * @flags:  AVC_STRICT or 0
Linus Torvalds's avatar
Linus Torvalds committed
708
709
710
711
712
713
714
715
716
717
718
719
720
721
 * @avd: access vector decisions
 *
 * Check the AVC to determine whether the @requested permissions are granted
 * for the SID pair (@ssid, @tsid), interpreting the permissions
 * based on @tclass, and call the security server on a cache miss to obtain
 * a new decision and add it to the cache.  Return a copy of the decisions
 * in @avd.  Return %0 if all @requested permissions are granted,
 * -%EACCES if any permissions are denied, or another -errno upon
 * other errors.  This function is typically called by avc_has_perm(),
 * but may also be called directly to separate permission checking from
 * auditing, e.g. in cases where a lock must be held for the check but
 * should be released for the auditing.
 */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
722
723
			 u16 tclass, u32 requested,
			 unsigned flags,
724
			 struct av_decision *in_avd)
Linus Torvalds's avatar
Linus Torvalds committed
725
726
{
	struct avc_node *node;
727
	struct av_decision avd_entry, *avd;
Linus Torvalds's avatar
Linus Torvalds committed
728
729
730
	int rc = 0;
	u32 denied;

731
732
	BUG_ON(!requested);

Linus Torvalds's avatar
Linus Torvalds committed
733
734
	rcu_read_lock();

735
	node = avc_lookup(ssid, tsid, tclass);
Linus Torvalds's avatar
Linus Torvalds committed
736
737
	if (!node) {
		rcu_read_unlock();
738
739
740
741
742
743

		if (in_avd)
			avd = in_avd;
		else
			avd = &avd_entry;

744
		security_compute_av(ssid, tsid, tclass, avd);
Linus Torvalds's avatar
Linus Torvalds committed
745
		rcu_read_lock();
746
747
748
749
750
		node = avc_insert(ssid, tsid, tclass, avd);
	} else {
		if (in_avd)
			memcpy(in_avd, &node->ae.avd, sizeof(*in_avd));
		avd = &node->ae.avd;
Linus Torvalds's avatar
Linus Torvalds committed
751
752
	}

753
	denied = requested & ~(avd->allowed);
Linus Torvalds's avatar
Linus Torvalds committed
754

755
	if (denied) {
756
		if (flags & AVC_STRICT)
Linus Torvalds's avatar
Linus Torvalds committed
757
			rc = -EACCES;
758
		else if (!selinux_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE))
759
			avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
760
					tsid, tclass, avd->seqno);
Linus Torvalds's avatar
Linus Torvalds committed
761
		else
762
			rc = -EACCES;
Linus Torvalds's avatar
Linus Torvalds committed
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	}

	rcu_read_unlock();
	return rc;
}

/**
 * avc_has_perm - Check permissions and perform any appropriate auditing.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 * @requested: requested permissions, interpreted based on @tclass
 * @auditdata: auxiliary audit data
 *
 * Check the AVC to determine whether the @requested permissions are granted
 * for the SID pair (@ssid, @tsid), interpreting the permissions
 * based on @tclass, and call the security server on a cache miss to obtain
 * a new decision and add it to the cache.  Audit the granting or denial of
 * permissions in accordance with the policy.  Return %0 if all @requested
 * permissions are granted, -%EACCES if any permissions are denied, or
 * another -errno upon other errors.
 */
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
786
		 u32 requested, struct common_audit_data *auditdata)
Linus Torvalds's avatar
Linus Torvalds committed
787
788
789
790
{
	struct av_decision avd;
	int rc;

791
	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
Linus Torvalds's avatar
Linus Torvalds committed
792
793
794
	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
	return rc;
}
795
796
797
798
799

u32 avc_policy_seqno(void)
{
	return avc_cache.latest_notif;
}
800
801
802

void avc_disable(void)
{
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
	/*
	 * If you are looking at this because you have realized that we are
	 * not destroying the avc_node_cachep it might be easy to fix, but
	 * I don't know the memory barrier semantics well enough to know.  It's
	 * possible that some other task dereferenced security_ops when
	 * it still pointed to selinux operations.  If that is the case it's
	 * possible that it is about to use the avc and is about to need the
	 * avc_node_cachep.  I know I could wrap the security.c security_ops call
	 * in an rcu_lock, but seriously, it's not worth it.  Instead I just flush
	 * the cache and get that memory back.
	 */
	if (avc_node_cachep) {
		avc_flush();
		/* kmem_cache_destroy(avc_node_cachep); */
	}
818
}