blob: e7ad31de205bda4ae42e9d63e79f8e7a34a789b0 [file] [log] [blame]
Joshua Brindle13cd4c82008-08-19 15:30:36 -04001/*
2 * Implementation of the userspace access vector cache (AVC).
3 *
4 * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
5 *
6 * Derived from the kernel AVC implementation by
7 * Stephen Smalley <sds@epoch.ncsc.mil> and
8 * James Morris <jmorris@redhat.com>.
9 */
10#include <selinux/avc.h>
11#include "selinux_internal.h"
12#include "avc_sidtab.h"
13#include "avc_internal.h"
14
15#define AVC_CACHE_SLOTS 512
16#define AVC_CACHE_MAXNODES 410
17
18struct avc_entry {
19 security_id_t ssid;
20 security_id_t tsid;
21 security_class_t tclass;
22 struct av_decision avd;
Eamon Walsh72394802009-03-11 19:01:42 -040023 security_id_t create_sid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -040024 int used; /* used recently */
25};
26
27struct avc_node {
28 struct avc_entry ae;
29 struct avc_node *next;
30};
31
32struct avc_cache {
33 struct avc_node *slots[AVC_CACHE_SLOTS];
34 uint32_t lru_hint; /* LRU hint for reclaim scan */
35 uint32_t active_nodes;
36 uint32_t latest_notif; /* latest revocation notification */
37};
38
39struct avc_callback_node {
40 int (*callback) (uint32_t event, security_id_t ssid,
41 security_id_t tsid,
42 security_class_t tclass, access_vector_t perms,
43 access_vector_t * out_retained);
44 uint32_t events;
45 security_id_t ssid;
46 security_id_t tsid;
47 security_class_t tclass;
48 access_vector_t perms;
49 struct avc_callback_node *next;
50};
51
52static void *avc_netlink_thread = NULL;
53static void *avc_lock = NULL;
54static void *avc_log_lock = NULL;
55static struct avc_node *avc_node_freelist = NULL;
56static struct avc_cache avc_cache;
57static char *avc_audit_buf = NULL;
58static struct avc_cache_stats cache_stats;
59static struct avc_callback_node *avc_callbacks = NULL;
60static struct sidtab avc_sidtab;
61
62static inline int avc_hash(security_id_t ssid,
63 security_id_t tsid, security_class_t tclass)
64{
65 return ((uintptr_t) ssid ^ ((uintptr_t) tsid << 2) ^ tclass)
66 & (AVC_CACHE_SLOTS - 1);
67}
68
KaiGai Kohei6a17cfa2010-06-14 15:21:51 -040069int avc_context_to_sid_raw(const security_context_t ctx, security_id_t * sid)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040070{
71 int rc;
72 avc_get_lock(avc_lock);
73 rc = sidtab_context_to_sid(&avc_sidtab, ctx, sid);
Joshua Brindle13cd4c82008-08-19 15:30:36 -040074 avc_release_lock(avc_lock);
75 return rc;
76}
77
KaiGai Kohei6a17cfa2010-06-14 15:21:51 -040078int avc_context_to_sid(const security_context_t ctx, security_id_t * sid)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040079{
80 int ret;
81 security_context_t rctx;
82
83 if (selinux_trans_to_raw_context(ctx, &rctx))
84 return -1;
85
86 ret = avc_context_to_sid_raw(rctx, sid);
87
88 freecon(rctx);
89
90 return ret;
91}
92
93int avc_sid_to_context_raw(security_id_t sid, security_context_t * ctx)
94{
95 int rc;
96 *ctx = NULL;
97 avc_get_lock(avc_lock);
Eamon Walsh58866dd2009-09-02 17:41:22 -040098 *ctx = strdup(sid->ctx); /* caller must free via freecon */
99 rc = *ctx ? 0 : -1;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400100 avc_release_lock(avc_lock);
101 return rc;
102}
103
104int avc_sid_to_context(security_id_t sid, security_context_t * ctx)
105{
106 int ret;
107 security_context_t rctx;
108
109 ret = avc_sid_to_context_raw(sid, &rctx);
110
111 if (ret == 0) {
112 ret = selinux_raw_to_trans_context(rctx, ctx);
113 freecon(rctx);
114 }
115
116 return ret;
117}
118
Eamon Walsh58866dd2009-09-02 17:41:22 -0400119int sidget(security_id_t sid __attribute__((unused)))
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400120{
Eamon Walsh58866dd2009-09-02 17:41:22 -0400121 return 1;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400122}
123
Eamon Walsh58866dd2009-09-02 17:41:22 -0400124int sidput(security_id_t sid __attribute__((unused)))
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400125{
Eamon Walsh58866dd2009-09-02 17:41:22 -0400126 return 1;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400127}
128
129int avc_get_initial_sid(const char * name, security_id_t * sid)
130{
131 int rc;
132 security_context_t con;
133
134 rc = security_get_initial_context_raw(name, &con);
135 if (rc < 0)
136 return rc;
137 rc = avc_context_to_sid_raw(con, sid);
138
139 freecon(con);
140
141 return rc;
142}
143
144int avc_open(struct selinux_opt *opts, unsigned nopts)
145{
146 avc_setenforce = 0;
147
148 while (nopts--)
149 switch(opts[nopts].type) {
150 case AVC_OPT_SETENFORCE:
151 avc_setenforce = 1;
152 avc_enforcing = !!opts[nopts].value;
153 break;
154 }
155
156 return avc_init("avc", NULL, NULL, NULL, NULL);
157}
158
159int avc_init(const char *prefix,
160 const struct avc_memory_callback *mem_cb,
161 const struct avc_log_callback *log_cb,
162 const struct avc_thread_callback *thread_cb,
163 const struct avc_lock_callback *lock_cb)
164{
165 struct avc_node *new;
166 int i, rc = 0;
167
Dan Walsh3b5e45f2011-10-20 15:43:12 -0400168 if (avc_running)
169 return 0;
170
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400171 if (prefix)
172 strncpy(avc_prefix, prefix, AVC_PREFIX_SIZE - 1);
173
174 set_callbacks(mem_cb, log_cb, thread_cb, lock_cb);
175
176 avc_lock = avc_alloc_lock();
177 avc_log_lock = avc_alloc_lock();
178
179 memset(&cache_stats, 0, sizeof(cache_stats));
180
181 for (i = 0; i < AVC_CACHE_SLOTS; i++)
182 avc_cache.slots[i] = 0;
183 avc_cache.lru_hint = 0;
184 avc_cache.active_nodes = 0;
185 avc_cache.latest_notif = 0;
186
187 rc = sidtab_init(&avc_sidtab);
188 if (rc) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400189 avc_log(SELINUX_ERROR,
190 "%s: unable to initialize SID table\n",
191 avc_prefix);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400192 goto out;
193 }
194
195 avc_audit_buf = (char *)avc_malloc(AVC_AUDIT_BUFSIZE);
196 if (!avc_audit_buf) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400197 avc_log(SELINUX_ERROR,
198 "%s: unable to allocate audit buffer\n",
199 avc_prefix);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400200 rc = -1;
201 goto out;
202 }
203
204 for (i = 0; i < AVC_CACHE_MAXNODES; i++) {
205 new = avc_malloc(sizeof(*new));
206 if (!new) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400207 avc_log(SELINUX_WARNING,
208 "%s: warning: only got %d av entries\n",
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400209 avc_prefix, i);
210 break;
211 }
212 memset(new, 0, sizeof(*new));
213 new->next = avc_node_freelist;
214 avc_node_freelist = new;
215 }
216
217 if (!avc_setenforce) {
218 rc = security_getenforce();
219 if (rc < 0) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400220 avc_log(SELINUX_ERROR,
Eamon Walshdbbd0ab2010-03-15 18:40:40 -0400221 "%s: could not determine enforcing mode: %s\n",
222 avc_prefix,
223 strerror(errno));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400224 goto out;
225 }
226 avc_enforcing = rc;
227 }
228
Eamon Walsh61d005b2010-02-26 15:18:38 -0500229 rc = avc_netlink_open(0);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400230 if (rc < 0) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400231 avc_log(SELINUX_ERROR,
232 "%s: can't open netlink socket: %d (%s)\n",
233 avc_prefix, errno, strerror(errno));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400234 goto out;
235 }
236 if (avc_using_threads) {
237 avc_netlink_thread = avc_create_thread(&avc_netlink_loop);
238 avc_netlink_trouble = 0;
239 }
240 avc_running = 1;
241 out:
242 return rc;
243}
244
245void avc_cache_stats(struct avc_cache_stats *p)
246{
247 memcpy(p, &cache_stats, sizeof(cache_stats));
248}
249
250void avc_sid_stats(void)
251{
252 avc_get_lock(avc_log_lock);
253 avc_get_lock(avc_lock);
254 sidtab_sid_stats(&avc_sidtab, avc_audit_buf, AVC_AUDIT_BUFSIZE);
255 avc_release_lock(avc_lock);
Eamon Walsheee0f022008-10-31 10:20:33 -0400256 avc_log(SELINUX_INFO, "%s", avc_audit_buf);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400257 avc_release_lock(avc_log_lock);
258}
259
260void avc_av_stats(void)
261{
262 int i, chain_len, max_chain_len, slots_used;
263 struct avc_node *node;
264
265 avc_get_lock(avc_lock);
266
267 slots_used = 0;
268 max_chain_len = 0;
269 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
270 node = avc_cache.slots[i];
271 if (node) {
272 slots_used++;
273 chain_len = 0;
274 while (node) {
275 chain_len++;
276 node = node->next;
277 }
278 if (chain_len > max_chain_len)
279 max_chain_len = chain_len;
280 }
281 }
282
283 avc_release_lock(avc_lock);
284
Eamon Walsheee0f022008-10-31 10:20:33 -0400285 avc_log(SELINUX_INFO, "%s: %d AV entries and %d/%d buckets used, "
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400286 "longest chain length %d\n", avc_prefix,
287 avc_cache.active_nodes,
288 slots_used, AVC_CACHE_SLOTS, max_chain_len);
289}
290
291hidden_def(avc_av_stats)
292
293static inline struct avc_node *avc_reclaim_node(void)
294{
295 struct avc_node *prev, *cur;
296 int try;
297 uint32_t hvalue;
298
299 hvalue = avc_cache.lru_hint;
300 for (try = 0; try < 2; try++) {
301 do {
302 prev = NULL;
303 cur = avc_cache.slots[hvalue];
304 while (cur) {
305 if (!cur->ae.used)
306 goto found;
307
308 cur->ae.used = 0;
309
310 prev = cur;
311 cur = cur->next;
312 }
313 hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1);
314 } while (hvalue != avc_cache.lru_hint);
315 }
316
317 errno = ENOMEM; /* this was a panic in the kernel... */
318 return NULL;
319
320 found:
321 avc_cache.lru_hint = hvalue;
322
323 if (prev == NULL)
324 avc_cache.slots[hvalue] = cur->next;
325 else
326 prev->next = cur->next;
327
328 return cur;
329}
330
Eamon Walsh72394802009-03-11 19:01:42 -0400331static inline void avc_clear_avc_entry(struct avc_entry *ae)
332{
333 ae->ssid = ae->tsid = ae->create_sid = NULL;
334 ae->tclass = 0;
335 ae->avd.allowed = ae->avd.decided = 0;
336 ae->avd.auditallow = ae->avd.auditdeny = 0;
337 ae->used = 0;
338}
339
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400340static inline struct avc_node *avc_claim_node(security_id_t ssid,
341 security_id_t tsid,
342 security_class_t tclass)
343{
344 struct avc_node *new;
345 int hvalue;
346
347 if (!avc_node_freelist)
348 avc_cleanup();
349
350 if (avc_node_freelist) {
351 new = avc_node_freelist;
352 avc_node_freelist = avc_node_freelist->next;
353 avc_cache.active_nodes++;
354 } else {
355 new = avc_reclaim_node();
356 if (!new)
357 goto out;
358 }
359
360 hvalue = avc_hash(ssid, tsid, tclass);
Eamon Walsh72394802009-03-11 19:01:42 -0400361 avc_clear_avc_entry(&new->ae);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400362 new->ae.used = 1;
363 new->ae.ssid = ssid;
364 new->ae.tsid = tsid;
365 new->ae.tclass = tclass;
366 new->next = avc_cache.slots[hvalue];
367 avc_cache.slots[hvalue] = new;
368
369 out:
370 return new;
371}
372
373static inline struct avc_node *avc_search_node(security_id_t ssid,
374 security_id_t tsid,
375 security_class_t tclass,
376 int *probes)
377{
378 struct avc_node *cur;
379 int hvalue;
380 int tprobes = 1;
381
382 hvalue = avc_hash(ssid, tsid, tclass);
383 cur = avc_cache.slots[hvalue];
384 while (cur != NULL &&
385 (ssid != cur->ae.ssid ||
386 tclass != cur->ae.tclass || tsid != cur->ae.tsid)) {
387 tprobes++;
388 cur = cur->next;
389 }
390
391 if (cur == NULL) {
392 /* cache miss */
393 goto out;
394 }
395
396 /* cache hit */
397 if (probes)
398 *probes = tprobes;
399
400 cur->ae.used = 1;
401
402 out:
403 return cur;
404}
405
406/**
407 * avc_lookup - Look up an AVC entry.
408 * @ssid: source security identifier
409 * @tsid: target security identifier
410 * @tclass: target security class
411 * @requested: requested permissions, interpreted based on @tclass
412 * @aeref: AVC entry reference
413 *
414 * Look up an AVC entry that is valid for the
415 * @requested permissions between the SID pair
416 * (@ssid, @tsid), interpreting the permissions
417 * based on @tclass. If a valid AVC entry exists,
418 * then this function updates @aeref to refer to the
419 * entry and returns %0. Otherwise, -1 is returned.
420 */
421static int avc_lookup(security_id_t ssid, security_id_t tsid,
422 security_class_t tclass,
423 access_vector_t requested, struct avc_entry_ref *aeref)
424{
425 struct avc_node *node;
426 int probes, rc = 0;
427
428 avc_cache_stats_incr(cav_lookups);
429 node = avc_search_node(ssid, tsid, tclass, &probes);
430
431 if (node && ((node->ae.avd.decided & requested) == requested)) {
432 avc_cache_stats_incr(cav_hits);
433 avc_cache_stats_add(cav_probes, probes);
434 aeref->ae = &node->ae;
435 goto out;
436 }
437
438 avc_cache_stats_incr(cav_misses);
439 rc = -1;
440 out:
441 return rc;
442}
443
444/**
445 * avc_insert - Insert an AVC entry.
446 * @ssid: source security identifier
447 * @tsid: target security identifier
448 * @tclass: target security class
449 * @ae: AVC entry
450 * @aeref: AVC entry reference
451 *
452 * Insert an AVC entry for the SID pair
453 * (@ssid, @tsid) and class @tclass.
454 * The access vectors and the sequence number are
455 * normally provided by the security server in
456 * response to a security_compute_av() call. If the
457 * sequence number @ae->avd.seqno is not less than the latest
458 * revocation notification, then the function copies
459 * the access vectors into a cache entry, updates
460 * @aeref to refer to the entry, and returns %0.
461 * Otherwise, this function returns -%1 with @errno set to %EAGAIN.
462 */
463static int avc_insert(security_id_t ssid, security_id_t tsid,
464 security_class_t tclass,
465 struct avc_entry *ae, struct avc_entry_ref *aeref)
466{
467 struct avc_node *node;
468 int rc = 0;
469
470 if (ae->avd.seqno < avc_cache.latest_notif) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400471 avc_log(SELINUX_WARNING,
472 "%s: seqno %d < latest_notif %d\n", avc_prefix,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400473 ae->avd.seqno, avc_cache.latest_notif);
474 errno = EAGAIN;
475 rc = -1;
476 goto out;
477 }
478
479 node = avc_claim_node(ssid, tsid, tclass);
480 if (!node) {
481 rc = -1;
482 goto out;
483 }
484
485 node->ae.avd.allowed = ae->avd.allowed;
486 node->ae.avd.decided = ae->avd.decided;
487 node->ae.avd.auditallow = ae->avd.auditallow;
488 node->ae.avd.auditdeny = ae->avd.auditdeny;
489 node->ae.avd.seqno = ae->avd.seqno;
490 aeref->ae = &node->ae;
491 out:
492 return rc;
493}
494
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400495void avc_cleanup(void)
496{
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400497}
498
499hidden_def(avc_cleanup)
500
501int avc_reset(void)
502{
503 struct avc_callback_node *c;
504 int i, ret, rc = 0, errsave = 0;
505 struct avc_node *node, *tmp;
506 errno = 0;
507
508 if (!avc_running)
509 return 0;
510
511 avc_get_lock(avc_lock);
512
513 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
514 node = avc_cache.slots[i];
515 while (node) {
516 tmp = node;
517 node = node->next;
Eamon Walsh72394802009-03-11 19:01:42 -0400518 avc_clear_avc_entry(&tmp->ae);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400519 tmp->next = avc_node_freelist;
520 avc_node_freelist = tmp;
521 avc_cache.active_nodes--;
522 }
523 avc_cache.slots[i] = 0;
524 }
525 avc_cache.lru_hint = 0;
526
527 avc_release_lock(avc_lock);
528
529 memset(&cache_stats, 0, sizeof(cache_stats));
530
531 for (c = avc_callbacks; c; c = c->next) {
532 if (c->events & AVC_CALLBACK_RESET) {
533 ret = c->callback(AVC_CALLBACK_RESET, 0, 0, 0, 0, 0);
534 if (ret && !rc) {
535 rc = ret;
536 errsave = errno;
537 }
538 }
539 }
540 errno = errsave;
541 return rc;
542}
543
544hidden_def(avc_reset)
545
546void avc_destroy(void)
547{
548 struct avc_callback_node *c;
549 struct avc_node *node, *tmp;
550 int i;
551
552 avc_get_lock(avc_lock);
553
554 if (avc_using_threads)
555 avc_stop_thread(avc_netlink_thread);
556 avc_netlink_close();
557
558 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
559 node = avc_cache.slots[i];
560 while (node) {
561 tmp = node;
562 node = node->next;
563 avc_free(tmp);
564 }
565 }
566 while (avc_node_freelist) {
567 tmp = avc_node_freelist;
568 avc_node_freelist = tmp->next;
569 avc_free(tmp);
570 }
571 avc_release_lock(avc_lock);
572
573 while (avc_callbacks) {
574 c = avc_callbacks;
575 avc_callbacks = c->next;
576 avc_free(c);
577 }
578 sidtab_destroy(&avc_sidtab);
579 avc_free_lock(avc_lock);
580 avc_free_lock(avc_log_lock);
581 avc_free(avc_audit_buf);
582 avc_running = 0;
583}
584
585/* ratelimit stuff put aside for now --EFW */
586#if 0
587/*
588 * Copied from net/core/utils.c:net_ratelimit and modified for
589 * use by the AVC audit facility.
590 */
591#define AVC_MSG_COST 5*HZ
592#define AVC_MSG_BURST 10*5*HZ
593
594/*
595 * This enforces a rate limit: not more than one kernel message
596 * every 5secs to make a denial-of-service attack impossible.
597 */
598static int avc_ratelimit(void)
599{
600 static unsigned long toks = 10 * 5 * HZ;
601 static unsigned long last_msg;
602 static int missed, rc = 0;
603 unsigned long now = jiffies;
604 void *ratelimit_lock = avc_alloc_lock();
605
606 avc_get_lock(ratelimit_lock);
607 toks += now - last_msg;
608 last_msg = now;
609 if (toks > AVC_MSG_BURST)
610 toks = AVC_MSG_BURST;
611 if (toks >= AVC_MSG_COST) {
612 int lost = missed;
613 missed = 0;
614 toks -= AVC_MSG_COST;
615 avc_release_lock(ratelimit_lock);
616 if (lost) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400617 avc_log(SELINUX_WARNING,
618 "%s: %d messages suppressed.\n", avc_prefix,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400619 lost);
620 }
621 rc = 1;
622 goto out;
623 }
624 missed++;
625 avc_release_lock(ratelimit_lock);
626 out:
627 avc_free_lock(ratelimit_lock);
628 return rc;
629}
630
631static inline int check_avc_ratelimit(void)
632{
633 if (avc_enforcing)
634 return avc_ratelimit();
635 else {
636 /* If permissive, then never suppress messages. */
637 return 1;
638 }
639}
640#endif /* ratelimit stuff */
641
642/**
643 * avc_dump_av - Display an access vector in human-readable form.
644 * @tclass: target security class
645 * @av: access vector
646 */
647static void avc_dump_av(security_class_t tclass, access_vector_t av)
648{
649 const char *permstr;
650 access_vector_t bit = 1;
651
652 if (av == 0) {
653 log_append(avc_audit_buf, " null");
654 return;
655 }
656
657 log_append(avc_audit_buf, " {");
658
659 while (av) {
660 if (av & bit) {
661 permstr = security_av_perm_to_string(tclass, bit);
662 if (!permstr)
663 break;
664 log_append(avc_audit_buf, " %s", permstr);
665 av &= ~bit;
666 }
667 bit <<= 1;
668 }
669
670 if (av)
671 log_append(avc_audit_buf, " 0x%x", av);
672 log_append(avc_audit_buf, " }");
673}
674
675/**
676 * avc_dump_query - Display a SID pair and a class in human-readable form.
677 * @ssid: source security identifier
678 * @tsid: target security identifier
679 * @tclass: target security class
680 */
681static void avc_dump_query(security_id_t ssid, security_id_t tsid,
682 security_class_t tclass)
683{
684 avc_get_lock(avc_lock);
685
Eamon Walsh58866dd2009-09-02 17:41:22 -0400686 log_append(avc_audit_buf, "scontext=%s tcontext=%s",
687 ssid->ctx, tsid->ctx);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400688
689 avc_release_lock(avc_lock);
690 log_append(avc_audit_buf, " tclass=%s",
691 security_class_to_string(tclass));
692}
693
694void avc_audit(security_id_t ssid, security_id_t tsid,
695 security_class_t tclass, access_vector_t requested,
696 struct av_decision *avd, int result, void *a)
697{
698 access_vector_t denied, audited;
699
700 denied = requested & ~avd->allowed;
Stephen Smalley0fc6c772010-02-05 09:24:41 -0500701 if (denied)
702 audited = denied & avd->auditdeny;
703 else if (!requested || result)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400704 audited = denied = requested;
Stephen Smalley0fc6c772010-02-05 09:24:41 -0500705 else
706 audited = requested & avd->auditallow;
707 if (!audited)
708 return;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400709#if 0
710 if (!check_avc_ratelimit())
711 return;
712#endif
713 /* prevent overlapping buffer writes */
714 avc_get_lock(avc_log_lock);
715 snprintf(avc_audit_buf, AVC_AUDIT_BUFSIZE,
716 "%s: %s ", avc_prefix, (denied || !requested) ? "denied" : "granted");
717 avc_dump_av(tclass, audited);
718 log_append(avc_audit_buf, " for ");
719
720 /* get any extra information printed by the callback */
721 avc_suppl_audit(a, tclass, avc_audit_buf + strlen(avc_audit_buf),
722 AVC_AUDIT_BUFSIZE - strlen(avc_audit_buf));
723
724 log_append(avc_audit_buf, " ");
725 avc_dump_query(ssid, tsid, tclass);
726 log_append(avc_audit_buf, "\n");
Eamon Walsheee0f022008-10-31 10:20:33 -0400727 avc_log(SELINUX_AVC, "%s", avc_audit_buf);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400728
729 avc_release_lock(avc_log_lock);
730}
731
732hidden_def(avc_audit)
733
734int avc_has_perm_noaudit(security_id_t ssid,
735 security_id_t tsid,
736 security_class_t tclass,
737 access_vector_t requested,
738 struct avc_entry_ref *aeref, struct av_decision *avd)
739{
740 struct avc_entry *ae;
741 int rc = 0;
742 struct avc_entry entry;
743 access_vector_t denied;
744 struct avc_entry_ref ref;
745
Eamon Walsh7ab6b292009-03-10 20:31:38 -0400746 if (!avc_using_threads && !avc_app_main_loop) {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400747 (void)avc_netlink_check_nb();
748 }
749
750 if (!aeref) {
751 avc_entry_ref_init(&ref);
752 aeref = &ref;
753 }
754
755 avc_get_lock(avc_lock);
756 avc_cache_stats_incr(entry_lookups);
757 ae = aeref->ae;
758 if (ae) {
759 if (ae->ssid == ssid &&
760 ae->tsid == tsid &&
761 ae->tclass == tclass &&
762 ((ae->avd.decided & requested) == requested)) {
763 avc_cache_stats_incr(entry_hits);
764 ae->used = 1;
765 } else {
766 avc_cache_stats_incr(entry_discards);
767 ae = 0;
768 }
769 }
770
771 if (!ae) {
772 avc_cache_stats_incr(entry_misses);
773 rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
774 if (rc) {
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400775 rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
776 tclass, requested,
777 &entry.avd);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400778 if (rc)
779 goto out;
780 rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
781 if (rc)
782 goto out;
783 }
784 ae = aeref->ae;
785 }
786
787 if (avd)
788 memcpy(avd, &ae->avd, sizeof(*avd));
789
790 denied = requested & ~(ae->avd.allowed);
791
792 if (!requested || denied) {
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400793 if (!avc_enforcing ||
794 (ae->avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE))
795 ae->avd.allowed |= requested;
796 else {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400797 errno = EACCES;
798 rc = -1;
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400799 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400800 }
801
802 out:
803 avc_release_lock(avc_lock);
804 return rc;
805}
806
807hidden_def(avc_has_perm_noaudit)
808
809int avc_has_perm(security_id_t ssid, security_id_t tsid,
810 security_class_t tclass, access_vector_t requested,
811 struct avc_entry_ref *aeref, void *auditdata)
812{
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400813 struct av_decision avd;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400814 int errsave, rc;
815
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400816 memset(&avd, 0, sizeof(avd));
817
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400818 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
819 errsave = errno;
820 avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
821 errno = errsave;
822 return rc;
823}
824
825int avc_compute_create(security_id_t ssid, security_id_t tsid,
826 security_class_t tclass, security_id_t *newsid)
827{
828 int rc;
Eamon Walsh72394802009-03-11 19:01:42 -0400829 struct avc_entry_ref aeref;
830 struct avc_entry entry;
831 security_context_t ctx;
832
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400833 *newsid = NULL;
Eamon Walsh72394802009-03-11 19:01:42 -0400834 avc_entry_ref_init(&aeref);
835
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400836 avc_get_lock(avc_lock);
Eamon Walsh72394802009-03-11 19:01:42 -0400837
838 /* check for a cached entry */
839 rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
840 if (rc) {
841 /* need to make a cache entry for this tuple */
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400842 rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
843 tclass, 0, &entry.avd);
Eamon Walsh72394802009-03-11 19:01:42 -0400844 if (rc)
845 goto out;
846 rc = avc_insert(ssid, tsid, tclass, &entry, &aeref);
847 if (rc)
848 goto out;
849 }
850
851 /* check for a saved compute_create value */
852 if (!aeref.ae->create_sid) {
853 /* need to query the kernel policy */
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400854 rc = security_compute_create_raw(ssid->ctx, tsid->ctx, tclass,
855 &ctx);
856 if (rc)
857 goto out;
858 rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400859 freecon(ctx);
Eamon Walsh72394802009-03-11 19:01:42 -0400860 if (rc)
861 goto out;
862
863 aeref.ae->create_sid = *newsid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400864 } else {
Eamon Walsh72394802009-03-11 19:01:42 -0400865 /* found saved value */
866 *newsid = aeref.ae->create_sid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400867 }
Eamon Walsh72394802009-03-11 19:01:42 -0400868
869 rc = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400870out:
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400871 avc_release_lock(avc_lock);
872 return rc;
873}
874
875int avc_compute_member(security_id_t ssid, security_id_t tsid,
876 security_class_t tclass, security_id_t *newsid)
877{
878 int rc;
Eamon Walsh58866dd2009-09-02 17:41:22 -0400879 security_context_t ctx = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400880 *newsid = NULL;
881 avc_get_lock(avc_lock);
Eamon Walsh58866dd2009-09-02 17:41:22 -0400882
883 rc = security_compute_member_raw(ssid->ctx, tsid->ctx, tclass, &ctx);
884 if (rc)
885 goto out;
886 rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
887 freecon(ctx);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400888out:
889 avc_release_lock(avc_lock);
890 return rc;
891}
892
893int avc_add_callback(int (*callback) (uint32_t event, security_id_t ssid,
894 security_id_t tsid,
895 security_class_t tclass,
896 access_vector_t perms,
897 access_vector_t * out_retained),
898 uint32_t events, security_id_t ssid,
899 security_id_t tsid,
900 security_class_t tclass, access_vector_t perms)
901{
902 struct avc_callback_node *c;
903 int rc = 0;
904
905 c = avc_malloc(sizeof(*c));
906 if (!c) {
907 rc = -1;
908 goto out;
909 }
910
911 c->callback = callback;
912 c->events = events;
913 c->ssid = ssid;
914 c->tsid = tsid;
915 c->tclass = tclass;
916 c->perms = perms;
917 c->next = avc_callbacks;
918 avc_callbacks = c;
919 out:
920 return rc;
921}
922
923static inline int avc_sidcmp(security_id_t x, security_id_t y)
924{
925 return (x == y || x == SECSID_WILD || y == SECSID_WILD);
926}
927
928static inline void avc_update_node(uint32_t event, struct avc_node *node,
929 access_vector_t perms)
930{
931 switch (event) {
932 case AVC_CALLBACK_GRANT:
933 node->ae.avd.allowed |= perms;
934 break;
935 case AVC_CALLBACK_TRY_REVOKE:
936 case AVC_CALLBACK_REVOKE:
937 node->ae.avd.allowed &= ~perms;
938 break;
939 case AVC_CALLBACK_AUDITALLOW_ENABLE:
940 node->ae.avd.auditallow |= perms;
941 break;
942 case AVC_CALLBACK_AUDITALLOW_DISABLE:
943 node->ae.avd.auditallow &= ~perms;
944 break;
945 case AVC_CALLBACK_AUDITDENY_ENABLE:
946 node->ae.avd.auditdeny |= perms;
947 break;
948 case AVC_CALLBACK_AUDITDENY_DISABLE:
949 node->ae.avd.auditdeny &= ~perms;
950 break;
951 }
952}
953
954static int avc_update_cache(uint32_t event, security_id_t ssid,
955 security_id_t tsid, security_class_t tclass,
956 access_vector_t perms)
957{
958 struct avc_node *node;
959 int i;
960
961 avc_get_lock(avc_lock);
962
963 if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
964 /* apply to all matching nodes */
965 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
966 for (node = avc_cache.slots[i]; node; node = node->next) {
967 if (avc_sidcmp(ssid, node->ae.ssid) &&
968 avc_sidcmp(tsid, node->ae.tsid) &&
969 tclass == node->ae.tclass) {
970 avc_update_node(event, node, perms);
971 }
972 }
973 }
974 } else {
975 /* apply to one node */
976 node = avc_search_node(ssid, tsid, tclass, 0);
977 if (node) {
978 avc_update_node(event, node, perms);
979 }
980 }
981
982 avc_release_lock(avc_lock);
983
984 return 0;
985}
986
987/* avc_control - update cache and call callbacks
988 *
989 * This should not be called directly; use the individual event
990 * functions instead.
991 */
992static int avc_control(uint32_t event, security_id_t ssid,
993 security_id_t tsid, security_class_t tclass,
994 access_vector_t perms,
995 uint32_t seqno, access_vector_t * out_retained)
996{
997 struct avc_callback_node *c;
998 access_vector_t tretained = 0, cretained = 0;
999 int ret, rc = 0, errsave = 0;
1000 errno = 0;
1001
1002 /*
1003 * try_revoke only removes permissions from the cache
1004 * state if they are not retained by the object manager.
1005 * Hence, try_revoke must wait until after the callbacks have
1006 * been invoked to update the cache state.
1007 */
1008 if (event != AVC_CALLBACK_TRY_REVOKE)
1009 avc_update_cache(event, ssid, tsid, tclass, perms);
1010
1011 for (c = avc_callbacks; c; c = c->next) {
1012 if ((c->events & event) &&
1013 avc_sidcmp(c->ssid, ssid) &&
1014 avc_sidcmp(c->tsid, tsid) &&
1015 c->tclass == tclass && (c->perms & perms)) {
1016 cretained = 0;
1017 ret = c->callback(event, ssid, tsid, tclass,
1018 (c->perms & perms), &cretained);
1019 if (ret && !rc) {
1020 rc = ret;
1021 errsave = errno;
1022 }
1023 if (!ret)
1024 tretained |= cretained;
1025 }
1026 }
1027
1028 if (event == AVC_CALLBACK_TRY_REVOKE) {
1029 /* revoke any unretained permissions */
1030 perms &= ~tretained;
1031 avc_update_cache(event, ssid, tsid, tclass, perms);
1032 *out_retained = tretained;
1033 }
1034
1035 avc_get_lock(avc_lock);
1036 if (seqno > avc_cache.latest_notif)
1037 avc_cache.latest_notif = seqno;
1038 avc_release_lock(avc_lock);
1039
1040 errno = errsave;
1041 return rc;
1042}
1043
1044/**
1045 * avc_ss_grant - Grant previously denied permissions.
1046 * @ssid: source security identifier or %SECSID_WILD
1047 * @tsid: target security identifier or %SECSID_WILD
1048 * @tclass: target security class
1049 * @perms: permissions to grant
1050 * @seqno: policy sequence number
1051 */
1052int avc_ss_grant(security_id_t ssid, security_id_t tsid,
1053 security_class_t tclass, access_vector_t perms,
1054 uint32_t seqno)
1055{
1056 return avc_control(AVC_CALLBACK_GRANT,
1057 ssid, tsid, tclass, perms, seqno, 0);
1058}
1059
1060/**
1061 * avc_ss_try_revoke - Try to revoke previously granted permissions.
1062 * @ssid: source security identifier or %SECSID_WILD
1063 * @tsid: target security identifier or %SECSID_WILD
1064 * @tclass: target security class
1065 * @perms: permissions to grant
1066 * @seqno: policy sequence number
1067 * @out_retained: subset of @perms that are retained
1068 *
1069 * Try to revoke previously granted permissions, but
1070 * only if they are not retained as migrated permissions.
1071 * Return the subset of permissions that are retained via @out_retained.
1072 */
1073int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
1074 security_class_t tclass,
1075 access_vector_t perms, uint32_t seqno,
1076 access_vector_t * out_retained)
1077{
1078 return avc_control(AVC_CALLBACK_TRY_REVOKE,
1079 ssid, tsid, tclass, perms, seqno, out_retained);
1080}
1081
1082/**
1083 * avc_ss_revoke - Revoke previously granted permissions.
1084 * @ssid: source security identifier or %SECSID_WILD
1085 * @tsid: target security identifier or %SECSID_WILD
1086 * @tclass: target security class
1087 * @perms: permissions to grant
1088 * @seqno: policy sequence number
1089 *
1090 * Revoke previously granted permissions, even if
1091 * they are retained as migrated permissions.
1092 */
1093int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
1094 security_class_t tclass, access_vector_t perms,
1095 uint32_t seqno)
1096{
1097 return avc_control(AVC_CALLBACK_REVOKE,
1098 ssid, tsid, tclass, perms, seqno, 0);
1099}
1100
1101/**
1102 * avc_ss_reset - Flush the cache and revalidate migrated permissions.
1103 * @seqno: policy sequence number
1104 */
1105int avc_ss_reset(uint32_t seqno)
1106{
1107 int rc;
1108
1109 rc = avc_reset();
1110
1111 avc_get_lock(avc_lock);
1112 if (seqno > avc_cache.latest_notif)
1113 avc_cache.latest_notif = seqno;
1114 avc_release_lock(avc_lock);
1115
1116 return rc;
1117}
1118
1119/**
1120 * avc_ss_set_auditallow - Enable or disable auditing of granted permissions.
1121 * @ssid: source security identifier or %SECSID_WILD
1122 * @tsid: target security identifier or %SECSID_WILD
1123 * @tclass: target security class
1124 * @perms: permissions to grant
1125 * @seqno: policy sequence number
1126 * @enable: enable flag.
1127 */
1128int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
1129 security_class_t tclass, access_vector_t perms,
1130 uint32_t seqno, uint32_t enable)
1131{
1132 if (enable)
1133 return avc_control(AVC_CALLBACK_AUDITALLOW_ENABLE,
1134 ssid, tsid, tclass, perms, seqno, 0);
1135 else
1136 return avc_control(AVC_CALLBACK_AUDITALLOW_DISABLE,
1137 ssid, tsid, tclass, perms, seqno, 0);
1138}
1139
1140/**
1141 * avc_ss_set_auditdeny - Enable or disable auditing of denied permissions.
1142 * @ssid: source security identifier or %SECSID_WILD
1143 * @tsid: target security identifier or %SECSID_WILD
1144 * @tclass: target security class
1145 * @perms: permissions to grant
1146 * @seqno: policy sequence number
1147 * @enable: enable flag.
1148 */
1149int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
1150 security_class_t tclass, access_vector_t perms,
1151 uint32_t seqno, uint32_t enable)
1152{
1153 if (enable)
1154 return avc_control(AVC_CALLBACK_AUDITDENY_ENABLE,
1155 ssid, tsid, tclass, perms, seqno, 0);
1156 else
1157 return avc_control(AVC_CALLBACK_AUDITDENY_DISABLE,
1158 ssid, tsid, tclass, perms, seqno, 0);
1159}