blob: 1c62fa38f7b39dc513ca24917baea22cce1d898e [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
69int avc_context_to_sid_raw(security_context_t ctx, security_id_t * sid)
70{
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
78int avc_context_to_sid(security_context_t ctx, security_id_t * sid)
79{
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
168 if (prefix)
169 strncpy(avc_prefix, prefix, AVC_PREFIX_SIZE - 1);
170
171 set_callbacks(mem_cb, log_cb, thread_cb, lock_cb);
172
173 avc_lock = avc_alloc_lock();
174 avc_log_lock = avc_alloc_lock();
175
176 memset(&cache_stats, 0, sizeof(cache_stats));
177
178 for (i = 0; i < AVC_CACHE_SLOTS; i++)
179 avc_cache.slots[i] = 0;
180 avc_cache.lru_hint = 0;
181 avc_cache.active_nodes = 0;
182 avc_cache.latest_notif = 0;
183
184 rc = sidtab_init(&avc_sidtab);
185 if (rc) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400186 avc_log(SELINUX_ERROR,
187 "%s: unable to initialize SID table\n",
188 avc_prefix);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400189 goto out;
190 }
191
192 avc_audit_buf = (char *)avc_malloc(AVC_AUDIT_BUFSIZE);
193 if (!avc_audit_buf) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400194 avc_log(SELINUX_ERROR,
195 "%s: unable to allocate audit buffer\n",
196 avc_prefix);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400197 rc = -1;
198 goto out;
199 }
200
201 for (i = 0; i < AVC_CACHE_MAXNODES; i++) {
202 new = avc_malloc(sizeof(*new));
203 if (!new) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400204 avc_log(SELINUX_WARNING,
205 "%s: warning: only got %d av entries\n",
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400206 avc_prefix, i);
207 break;
208 }
209 memset(new, 0, sizeof(*new));
210 new->next = avc_node_freelist;
211 avc_node_freelist = new;
212 }
213
214 if (!avc_setenforce) {
215 rc = security_getenforce();
216 if (rc < 0) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400217 avc_log(SELINUX_ERROR,
218 "%s: could not determine enforcing mode\n",
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400219 avc_prefix);
220 goto out;
221 }
222 avc_enforcing = rc;
223 }
224
225 rc = avc_netlink_open(avc_using_threads);
226 if (rc < 0) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400227 avc_log(SELINUX_ERROR,
228 "%s: can't open netlink socket: %d (%s)\n",
229 avc_prefix, errno, strerror(errno));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400230 goto out;
231 }
232 if (avc_using_threads) {
233 avc_netlink_thread = avc_create_thread(&avc_netlink_loop);
234 avc_netlink_trouble = 0;
235 }
236 avc_running = 1;
237 out:
238 return rc;
239}
240
241void avc_cache_stats(struct avc_cache_stats *p)
242{
243 memcpy(p, &cache_stats, sizeof(cache_stats));
244}
245
246void avc_sid_stats(void)
247{
248 avc_get_lock(avc_log_lock);
249 avc_get_lock(avc_lock);
250 sidtab_sid_stats(&avc_sidtab, avc_audit_buf, AVC_AUDIT_BUFSIZE);
251 avc_release_lock(avc_lock);
Eamon Walsheee0f022008-10-31 10:20:33 -0400252 avc_log(SELINUX_INFO, "%s", avc_audit_buf);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400253 avc_release_lock(avc_log_lock);
254}
255
256void avc_av_stats(void)
257{
258 int i, chain_len, max_chain_len, slots_used;
259 struct avc_node *node;
260
261 avc_get_lock(avc_lock);
262
263 slots_used = 0;
264 max_chain_len = 0;
265 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
266 node = avc_cache.slots[i];
267 if (node) {
268 slots_used++;
269 chain_len = 0;
270 while (node) {
271 chain_len++;
272 node = node->next;
273 }
274 if (chain_len > max_chain_len)
275 max_chain_len = chain_len;
276 }
277 }
278
279 avc_release_lock(avc_lock);
280
Eamon Walsheee0f022008-10-31 10:20:33 -0400281 avc_log(SELINUX_INFO, "%s: %d AV entries and %d/%d buckets used, "
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400282 "longest chain length %d\n", avc_prefix,
283 avc_cache.active_nodes,
284 slots_used, AVC_CACHE_SLOTS, max_chain_len);
285}
286
287hidden_def(avc_av_stats)
288
289static inline struct avc_node *avc_reclaim_node(void)
290{
291 struct avc_node *prev, *cur;
292 int try;
293 uint32_t hvalue;
294
295 hvalue = avc_cache.lru_hint;
296 for (try = 0; try < 2; try++) {
297 do {
298 prev = NULL;
299 cur = avc_cache.slots[hvalue];
300 while (cur) {
301 if (!cur->ae.used)
302 goto found;
303
304 cur->ae.used = 0;
305
306 prev = cur;
307 cur = cur->next;
308 }
309 hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1);
310 } while (hvalue != avc_cache.lru_hint);
311 }
312
313 errno = ENOMEM; /* this was a panic in the kernel... */
314 return NULL;
315
316 found:
317 avc_cache.lru_hint = hvalue;
318
319 if (prev == NULL)
320 avc_cache.slots[hvalue] = cur->next;
321 else
322 prev->next = cur->next;
323
324 return cur;
325}
326
Eamon Walsh72394802009-03-11 19:01:42 -0400327static inline void avc_clear_avc_entry(struct avc_entry *ae)
328{
329 ae->ssid = ae->tsid = ae->create_sid = NULL;
330 ae->tclass = 0;
331 ae->avd.allowed = ae->avd.decided = 0;
332 ae->avd.auditallow = ae->avd.auditdeny = 0;
333 ae->used = 0;
334}
335
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400336static inline struct avc_node *avc_claim_node(security_id_t ssid,
337 security_id_t tsid,
338 security_class_t tclass)
339{
340 struct avc_node *new;
341 int hvalue;
342
343 if (!avc_node_freelist)
344 avc_cleanup();
345
346 if (avc_node_freelist) {
347 new = avc_node_freelist;
348 avc_node_freelist = avc_node_freelist->next;
349 avc_cache.active_nodes++;
350 } else {
351 new = avc_reclaim_node();
352 if (!new)
353 goto out;
354 }
355
356 hvalue = avc_hash(ssid, tsid, tclass);
Eamon Walsh72394802009-03-11 19:01:42 -0400357 avc_clear_avc_entry(&new->ae);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400358 new->ae.used = 1;
359 new->ae.ssid = ssid;
360 new->ae.tsid = tsid;
361 new->ae.tclass = tclass;
362 new->next = avc_cache.slots[hvalue];
363 avc_cache.slots[hvalue] = new;
364
365 out:
366 return new;
367}
368
369static inline struct avc_node *avc_search_node(security_id_t ssid,
370 security_id_t tsid,
371 security_class_t tclass,
372 int *probes)
373{
374 struct avc_node *cur;
375 int hvalue;
376 int tprobes = 1;
377
378 hvalue = avc_hash(ssid, tsid, tclass);
379 cur = avc_cache.slots[hvalue];
380 while (cur != NULL &&
381 (ssid != cur->ae.ssid ||
382 tclass != cur->ae.tclass || tsid != cur->ae.tsid)) {
383 tprobes++;
384 cur = cur->next;
385 }
386
387 if (cur == NULL) {
388 /* cache miss */
389 goto out;
390 }
391
392 /* cache hit */
393 if (probes)
394 *probes = tprobes;
395
396 cur->ae.used = 1;
397
398 out:
399 return cur;
400}
401
402/**
403 * avc_lookup - Look up an AVC entry.
404 * @ssid: source security identifier
405 * @tsid: target security identifier
406 * @tclass: target security class
407 * @requested: requested permissions, interpreted based on @tclass
408 * @aeref: AVC entry reference
409 *
410 * Look up an AVC entry that is valid for the
411 * @requested permissions between the SID pair
412 * (@ssid, @tsid), interpreting the permissions
413 * based on @tclass. If a valid AVC entry exists,
414 * then this function updates @aeref to refer to the
415 * entry and returns %0. Otherwise, -1 is returned.
416 */
417static int avc_lookup(security_id_t ssid, security_id_t tsid,
418 security_class_t tclass,
419 access_vector_t requested, struct avc_entry_ref *aeref)
420{
421 struct avc_node *node;
422 int probes, rc = 0;
423
424 avc_cache_stats_incr(cav_lookups);
425 node = avc_search_node(ssid, tsid, tclass, &probes);
426
427 if (node && ((node->ae.avd.decided & requested) == requested)) {
428 avc_cache_stats_incr(cav_hits);
429 avc_cache_stats_add(cav_probes, probes);
430 aeref->ae = &node->ae;
431 goto out;
432 }
433
434 avc_cache_stats_incr(cav_misses);
435 rc = -1;
436 out:
437 return rc;
438}
439
440/**
441 * avc_insert - Insert an AVC entry.
442 * @ssid: source security identifier
443 * @tsid: target security identifier
444 * @tclass: target security class
445 * @ae: AVC entry
446 * @aeref: AVC entry reference
447 *
448 * Insert an AVC entry for the SID pair
449 * (@ssid, @tsid) and class @tclass.
450 * The access vectors and the sequence number are
451 * normally provided by the security server in
452 * response to a security_compute_av() call. If the
453 * sequence number @ae->avd.seqno is not less than the latest
454 * revocation notification, then the function copies
455 * the access vectors into a cache entry, updates
456 * @aeref to refer to the entry, and returns %0.
457 * Otherwise, this function returns -%1 with @errno set to %EAGAIN.
458 */
459static int avc_insert(security_id_t ssid, security_id_t tsid,
460 security_class_t tclass,
461 struct avc_entry *ae, struct avc_entry_ref *aeref)
462{
463 struct avc_node *node;
464 int rc = 0;
465
466 if (ae->avd.seqno < avc_cache.latest_notif) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400467 avc_log(SELINUX_WARNING,
468 "%s: seqno %d < latest_notif %d\n", avc_prefix,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400469 ae->avd.seqno, avc_cache.latest_notif);
470 errno = EAGAIN;
471 rc = -1;
472 goto out;
473 }
474
475 node = avc_claim_node(ssid, tsid, tclass);
476 if (!node) {
477 rc = -1;
478 goto out;
479 }
480
481 node->ae.avd.allowed = ae->avd.allowed;
482 node->ae.avd.decided = ae->avd.decided;
483 node->ae.avd.auditallow = ae->avd.auditallow;
484 node->ae.avd.auditdeny = ae->avd.auditdeny;
485 node->ae.avd.seqno = ae->avd.seqno;
486 aeref->ae = &node->ae;
487 out:
488 return rc;
489}
490
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400491void avc_cleanup(void)
492{
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400493}
494
495hidden_def(avc_cleanup)
496
497int avc_reset(void)
498{
499 struct avc_callback_node *c;
500 int i, ret, rc = 0, errsave = 0;
501 struct avc_node *node, *tmp;
502 errno = 0;
503
504 if (!avc_running)
505 return 0;
506
507 avc_get_lock(avc_lock);
508
509 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
510 node = avc_cache.slots[i];
511 while (node) {
512 tmp = node;
513 node = node->next;
Eamon Walsh72394802009-03-11 19:01:42 -0400514 avc_clear_avc_entry(&tmp->ae);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400515 tmp->next = avc_node_freelist;
516 avc_node_freelist = tmp;
517 avc_cache.active_nodes--;
518 }
519 avc_cache.slots[i] = 0;
520 }
521 avc_cache.lru_hint = 0;
522
523 avc_release_lock(avc_lock);
524
525 memset(&cache_stats, 0, sizeof(cache_stats));
526
527 for (c = avc_callbacks; c; c = c->next) {
528 if (c->events & AVC_CALLBACK_RESET) {
529 ret = c->callback(AVC_CALLBACK_RESET, 0, 0, 0, 0, 0);
530 if (ret && !rc) {
531 rc = ret;
532 errsave = errno;
533 }
534 }
535 }
536 errno = errsave;
537 return rc;
538}
539
540hidden_def(avc_reset)
541
542void avc_destroy(void)
543{
544 struct avc_callback_node *c;
545 struct avc_node *node, *tmp;
546 int i;
547
548 avc_get_lock(avc_lock);
549
550 if (avc_using_threads)
551 avc_stop_thread(avc_netlink_thread);
552 avc_netlink_close();
553
554 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
555 node = avc_cache.slots[i];
556 while (node) {
557 tmp = node;
558 node = node->next;
559 avc_free(tmp);
560 }
561 }
562 while (avc_node_freelist) {
563 tmp = avc_node_freelist;
564 avc_node_freelist = tmp->next;
565 avc_free(tmp);
566 }
567 avc_release_lock(avc_lock);
568
569 while (avc_callbacks) {
570 c = avc_callbacks;
571 avc_callbacks = c->next;
572 avc_free(c);
573 }
574 sidtab_destroy(&avc_sidtab);
575 avc_free_lock(avc_lock);
576 avc_free_lock(avc_log_lock);
577 avc_free(avc_audit_buf);
578 avc_running = 0;
579}
580
581/* ratelimit stuff put aside for now --EFW */
582#if 0
583/*
584 * Copied from net/core/utils.c:net_ratelimit and modified for
585 * use by the AVC audit facility.
586 */
587#define AVC_MSG_COST 5*HZ
588#define AVC_MSG_BURST 10*5*HZ
589
590/*
591 * This enforces a rate limit: not more than one kernel message
592 * every 5secs to make a denial-of-service attack impossible.
593 */
594static int avc_ratelimit(void)
595{
596 static unsigned long toks = 10 * 5 * HZ;
597 static unsigned long last_msg;
598 static int missed, rc = 0;
599 unsigned long now = jiffies;
600 void *ratelimit_lock = avc_alloc_lock();
601
602 avc_get_lock(ratelimit_lock);
603 toks += now - last_msg;
604 last_msg = now;
605 if (toks > AVC_MSG_BURST)
606 toks = AVC_MSG_BURST;
607 if (toks >= AVC_MSG_COST) {
608 int lost = missed;
609 missed = 0;
610 toks -= AVC_MSG_COST;
611 avc_release_lock(ratelimit_lock);
612 if (lost) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400613 avc_log(SELINUX_WARNING,
614 "%s: %d messages suppressed.\n", avc_prefix,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400615 lost);
616 }
617 rc = 1;
618 goto out;
619 }
620 missed++;
621 avc_release_lock(ratelimit_lock);
622 out:
623 avc_free_lock(ratelimit_lock);
624 return rc;
625}
626
627static inline int check_avc_ratelimit(void)
628{
629 if (avc_enforcing)
630 return avc_ratelimit();
631 else {
632 /* If permissive, then never suppress messages. */
633 return 1;
634 }
635}
636#endif /* ratelimit stuff */
637
638/**
639 * avc_dump_av - Display an access vector in human-readable form.
640 * @tclass: target security class
641 * @av: access vector
642 */
643static void avc_dump_av(security_class_t tclass, access_vector_t av)
644{
645 const char *permstr;
646 access_vector_t bit = 1;
647
648 if (av == 0) {
649 log_append(avc_audit_buf, " null");
650 return;
651 }
652
653 log_append(avc_audit_buf, " {");
654
655 while (av) {
656 if (av & bit) {
657 permstr = security_av_perm_to_string(tclass, bit);
658 if (!permstr)
659 break;
660 log_append(avc_audit_buf, " %s", permstr);
661 av &= ~bit;
662 }
663 bit <<= 1;
664 }
665
666 if (av)
667 log_append(avc_audit_buf, " 0x%x", av);
668 log_append(avc_audit_buf, " }");
669}
670
671/**
672 * avc_dump_query - Display a SID pair and a class in human-readable form.
673 * @ssid: source security identifier
674 * @tsid: target security identifier
675 * @tclass: target security class
676 */
677static void avc_dump_query(security_id_t ssid, security_id_t tsid,
678 security_class_t tclass)
679{
680 avc_get_lock(avc_lock);
681
Eamon Walsh58866dd2009-09-02 17:41:22 -0400682 log_append(avc_audit_buf, "scontext=%s tcontext=%s",
683 ssid->ctx, tsid->ctx);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400684
685 avc_release_lock(avc_lock);
686 log_append(avc_audit_buf, " tclass=%s",
687 security_class_to_string(tclass));
688}
689
690void avc_audit(security_id_t ssid, security_id_t tsid,
691 security_class_t tclass, access_vector_t requested,
692 struct av_decision *avd, int result, void *a)
693{
694 access_vector_t denied, audited;
695
696 denied = requested & ~avd->allowed;
697 if (denied) {
698 audited = denied;
699 if (!(audited & avd->auditdeny))
700 return;
701 } else if (!requested || result) {
702 audited = denied = requested;
703 } else {
704 audited = requested;
705 if (!(audited & avd->auditallow))
706 return;
707 }
708#if 0
709 if (!check_avc_ratelimit())
710 return;
711#endif
712 /* prevent overlapping buffer writes */
713 avc_get_lock(avc_log_lock);
714 snprintf(avc_audit_buf, AVC_AUDIT_BUFSIZE,
715 "%s: %s ", avc_prefix, (denied || !requested) ? "denied" : "granted");
716 avc_dump_av(tclass, audited);
717 log_append(avc_audit_buf, " for ");
718
719 /* get any extra information printed by the callback */
720 avc_suppl_audit(a, tclass, avc_audit_buf + strlen(avc_audit_buf),
721 AVC_AUDIT_BUFSIZE - strlen(avc_audit_buf));
722
723 log_append(avc_audit_buf, " ");
724 avc_dump_query(ssid, tsid, tclass);
725 log_append(avc_audit_buf, "\n");
Eamon Walsheee0f022008-10-31 10:20:33 -0400726 avc_log(SELINUX_AVC, "%s", avc_audit_buf);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400727
728 avc_release_lock(avc_log_lock);
729}
730
731hidden_def(avc_audit)
732
733int avc_has_perm_noaudit(security_id_t ssid,
734 security_id_t tsid,
735 security_class_t tclass,
736 access_vector_t requested,
737 struct avc_entry_ref *aeref, struct av_decision *avd)
738{
739 struct avc_entry *ae;
740 int rc = 0;
741 struct avc_entry entry;
742 access_vector_t denied;
743 struct avc_entry_ref ref;
744
Eamon Walsh7ab6b292009-03-10 20:31:38 -0400745 if (!avc_using_threads && !avc_app_main_loop) {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400746 (void)avc_netlink_check_nb();
747 }
748
749 if (!aeref) {
750 avc_entry_ref_init(&ref);
751 aeref = &ref;
752 }
753
754 avc_get_lock(avc_lock);
755 avc_cache_stats_incr(entry_lookups);
756 ae = aeref->ae;
757 if (ae) {
758 if (ae->ssid == ssid &&
759 ae->tsid == tsid &&
760 ae->tclass == tclass &&
761 ((ae->avd.decided & requested) == requested)) {
762 avc_cache_stats_incr(entry_hits);
763 ae->used = 1;
764 } else {
765 avc_cache_stats_incr(entry_discards);
766 ae = 0;
767 }
768 }
769
770 if (!ae) {
771 avc_cache_stats_incr(entry_misses);
772 rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
773 if (rc) {
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400774 rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
775 tclass, requested,
776 &entry.avd);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400777 if (rc)
778 goto out;
779 rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
780 if (rc)
781 goto out;
782 }
783 ae = aeref->ae;
784 }
785
786 if (avd)
787 memcpy(avd, &ae->avd, sizeof(*avd));
788
789 denied = requested & ~(ae->avd.allowed);
790
791 if (!requested || denied) {
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400792 if (!avc_enforcing ||
793 (ae->avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE))
794 ae->avd.allowed |= requested;
795 else {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400796 errno = EACCES;
797 rc = -1;
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400798 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400799 }
800
801 out:
802 avc_release_lock(avc_lock);
803 return rc;
804}
805
806hidden_def(avc_has_perm_noaudit)
807
808int avc_has_perm(security_id_t ssid, security_id_t tsid,
809 security_class_t tclass, access_vector_t requested,
810 struct avc_entry_ref *aeref, void *auditdata)
811{
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400812 struct av_decision avd;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400813 int errsave, rc;
814
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400815 memset(&avd, 0, sizeof(avd));
816
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400817 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
818 errsave = errno;
819 avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
820 errno = errsave;
821 return rc;
822}
823
824int avc_compute_create(security_id_t ssid, security_id_t tsid,
825 security_class_t tclass, security_id_t *newsid)
826{
827 int rc;
Eamon Walsh72394802009-03-11 19:01:42 -0400828 struct avc_entry_ref aeref;
829 struct avc_entry entry;
830 security_context_t ctx;
831
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400832 *newsid = NULL;
Eamon Walsh72394802009-03-11 19:01:42 -0400833 avc_entry_ref_init(&aeref);
834
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400835 avc_get_lock(avc_lock);
Eamon Walsh72394802009-03-11 19:01:42 -0400836
837 /* check for a cached entry */
838 rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
839 if (rc) {
840 /* need to make a cache entry for this tuple */
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400841 rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
842 tclass, 0, &entry.avd);
Eamon Walsh72394802009-03-11 19:01:42 -0400843 if (rc)
844 goto out;
845 rc = avc_insert(ssid, tsid, tclass, &entry, &aeref);
846 if (rc)
847 goto out;
848 }
849
850 /* check for a saved compute_create value */
851 if (!aeref.ae->create_sid) {
852 /* need to query the kernel policy */
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400853 rc = security_compute_create_raw(ssid->ctx, tsid->ctx, tclass,
854 &ctx);
855 if (rc)
856 goto out;
857 rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400858 freecon(ctx);
Eamon Walsh72394802009-03-11 19:01:42 -0400859 if (rc)
860 goto out;
861
862 aeref.ae->create_sid = *newsid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400863 } else {
Eamon Walsh72394802009-03-11 19:01:42 -0400864 /* found saved value */
865 *newsid = aeref.ae->create_sid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400866 }
Eamon Walsh72394802009-03-11 19:01:42 -0400867
868 rc = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400869out:
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400870 avc_release_lock(avc_lock);
871 return rc;
872}
873
874int avc_compute_member(security_id_t ssid, security_id_t tsid,
875 security_class_t tclass, security_id_t *newsid)
876{
877 int rc;
Eamon Walsh58866dd2009-09-02 17:41:22 -0400878 security_context_t ctx = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400879 *newsid = NULL;
880 avc_get_lock(avc_lock);
Eamon Walsh58866dd2009-09-02 17:41:22 -0400881
882 rc = security_compute_member_raw(ssid->ctx, tsid->ctx, tclass, &ctx);
883 if (rc)
884 goto out;
885 rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
886 freecon(ctx);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400887out:
888 avc_release_lock(avc_lock);
889 return rc;
890}
891
892int avc_add_callback(int (*callback) (uint32_t event, security_id_t ssid,
893 security_id_t tsid,
894 security_class_t tclass,
895 access_vector_t perms,
896 access_vector_t * out_retained),
897 uint32_t events, security_id_t ssid,
898 security_id_t tsid,
899 security_class_t tclass, access_vector_t perms)
900{
901 struct avc_callback_node *c;
902 int rc = 0;
903
904 c = avc_malloc(sizeof(*c));
905 if (!c) {
906 rc = -1;
907 goto out;
908 }
909
910 c->callback = callback;
911 c->events = events;
912 c->ssid = ssid;
913 c->tsid = tsid;
914 c->tclass = tclass;
915 c->perms = perms;
916 c->next = avc_callbacks;
917 avc_callbacks = c;
918 out:
919 return rc;
920}
921
922static inline int avc_sidcmp(security_id_t x, security_id_t y)
923{
924 return (x == y || x == SECSID_WILD || y == SECSID_WILD);
925}
926
927static inline void avc_update_node(uint32_t event, struct avc_node *node,
928 access_vector_t perms)
929{
930 switch (event) {
931 case AVC_CALLBACK_GRANT:
932 node->ae.avd.allowed |= perms;
933 break;
934 case AVC_CALLBACK_TRY_REVOKE:
935 case AVC_CALLBACK_REVOKE:
936 node->ae.avd.allowed &= ~perms;
937 break;
938 case AVC_CALLBACK_AUDITALLOW_ENABLE:
939 node->ae.avd.auditallow |= perms;
940 break;
941 case AVC_CALLBACK_AUDITALLOW_DISABLE:
942 node->ae.avd.auditallow &= ~perms;
943 break;
944 case AVC_CALLBACK_AUDITDENY_ENABLE:
945 node->ae.avd.auditdeny |= perms;
946 break;
947 case AVC_CALLBACK_AUDITDENY_DISABLE:
948 node->ae.avd.auditdeny &= ~perms;
949 break;
950 }
951}
952
953static int avc_update_cache(uint32_t event, security_id_t ssid,
954 security_id_t tsid, security_class_t tclass,
955 access_vector_t perms)
956{
957 struct avc_node *node;
958 int i;
959
960 avc_get_lock(avc_lock);
961
962 if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
963 /* apply to all matching nodes */
964 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
965 for (node = avc_cache.slots[i]; node; node = node->next) {
966 if (avc_sidcmp(ssid, node->ae.ssid) &&
967 avc_sidcmp(tsid, node->ae.tsid) &&
968 tclass == node->ae.tclass) {
969 avc_update_node(event, node, perms);
970 }
971 }
972 }
973 } else {
974 /* apply to one node */
975 node = avc_search_node(ssid, tsid, tclass, 0);
976 if (node) {
977 avc_update_node(event, node, perms);
978 }
979 }
980
981 avc_release_lock(avc_lock);
982
983 return 0;
984}
985
986/* avc_control - update cache and call callbacks
987 *
988 * This should not be called directly; use the individual event
989 * functions instead.
990 */
991static int avc_control(uint32_t event, security_id_t ssid,
992 security_id_t tsid, security_class_t tclass,
993 access_vector_t perms,
994 uint32_t seqno, access_vector_t * out_retained)
995{
996 struct avc_callback_node *c;
997 access_vector_t tretained = 0, cretained = 0;
998 int ret, rc = 0, errsave = 0;
999 errno = 0;
1000
1001 /*
1002 * try_revoke only removes permissions from the cache
1003 * state if they are not retained by the object manager.
1004 * Hence, try_revoke must wait until after the callbacks have
1005 * been invoked to update the cache state.
1006 */
1007 if (event != AVC_CALLBACK_TRY_REVOKE)
1008 avc_update_cache(event, ssid, tsid, tclass, perms);
1009
1010 for (c = avc_callbacks; c; c = c->next) {
1011 if ((c->events & event) &&
1012 avc_sidcmp(c->ssid, ssid) &&
1013 avc_sidcmp(c->tsid, tsid) &&
1014 c->tclass == tclass && (c->perms & perms)) {
1015 cretained = 0;
1016 ret = c->callback(event, ssid, tsid, tclass,
1017 (c->perms & perms), &cretained);
1018 if (ret && !rc) {
1019 rc = ret;
1020 errsave = errno;
1021 }
1022 if (!ret)
1023 tretained |= cretained;
1024 }
1025 }
1026
1027 if (event == AVC_CALLBACK_TRY_REVOKE) {
1028 /* revoke any unretained permissions */
1029 perms &= ~tretained;
1030 avc_update_cache(event, ssid, tsid, tclass, perms);
1031 *out_retained = tretained;
1032 }
1033
1034 avc_get_lock(avc_lock);
1035 if (seqno > avc_cache.latest_notif)
1036 avc_cache.latest_notif = seqno;
1037 avc_release_lock(avc_lock);
1038
1039 errno = errsave;
1040 return rc;
1041}
1042
1043/**
1044 * avc_ss_grant - Grant previously denied permissions.
1045 * @ssid: source security identifier or %SECSID_WILD
1046 * @tsid: target security identifier or %SECSID_WILD
1047 * @tclass: target security class
1048 * @perms: permissions to grant
1049 * @seqno: policy sequence number
1050 */
1051int avc_ss_grant(security_id_t ssid, security_id_t tsid,
1052 security_class_t tclass, access_vector_t perms,
1053 uint32_t seqno)
1054{
1055 return avc_control(AVC_CALLBACK_GRANT,
1056 ssid, tsid, tclass, perms, seqno, 0);
1057}
1058
1059/**
1060 * avc_ss_try_revoke - Try to revoke previously granted permissions.
1061 * @ssid: source security identifier or %SECSID_WILD
1062 * @tsid: target security identifier or %SECSID_WILD
1063 * @tclass: target security class
1064 * @perms: permissions to grant
1065 * @seqno: policy sequence number
1066 * @out_retained: subset of @perms that are retained
1067 *
1068 * Try to revoke previously granted permissions, but
1069 * only if they are not retained as migrated permissions.
1070 * Return the subset of permissions that are retained via @out_retained.
1071 */
1072int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
1073 security_class_t tclass,
1074 access_vector_t perms, uint32_t seqno,
1075 access_vector_t * out_retained)
1076{
1077 return avc_control(AVC_CALLBACK_TRY_REVOKE,
1078 ssid, tsid, tclass, perms, seqno, out_retained);
1079}
1080
1081/**
1082 * avc_ss_revoke - Revoke previously granted permissions.
1083 * @ssid: source security identifier or %SECSID_WILD
1084 * @tsid: target security identifier or %SECSID_WILD
1085 * @tclass: target security class
1086 * @perms: permissions to grant
1087 * @seqno: policy sequence number
1088 *
1089 * Revoke previously granted permissions, even if
1090 * they are retained as migrated permissions.
1091 */
1092int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
1093 security_class_t tclass, access_vector_t perms,
1094 uint32_t seqno)
1095{
1096 return avc_control(AVC_CALLBACK_REVOKE,
1097 ssid, tsid, tclass, perms, seqno, 0);
1098}
1099
1100/**
1101 * avc_ss_reset - Flush the cache and revalidate migrated permissions.
1102 * @seqno: policy sequence number
1103 */
1104int avc_ss_reset(uint32_t seqno)
1105{
1106 int rc;
1107
1108 rc = avc_reset();
1109
1110 avc_get_lock(avc_lock);
1111 if (seqno > avc_cache.latest_notif)
1112 avc_cache.latest_notif = seqno;
1113 avc_release_lock(avc_lock);
1114
1115 return rc;
1116}
1117
1118/**
1119 * avc_ss_set_auditallow - Enable or disable auditing of granted permissions.
1120 * @ssid: source security identifier or %SECSID_WILD
1121 * @tsid: target security identifier or %SECSID_WILD
1122 * @tclass: target security class
1123 * @perms: permissions to grant
1124 * @seqno: policy sequence number
1125 * @enable: enable flag.
1126 */
1127int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
1128 security_class_t tclass, access_vector_t perms,
1129 uint32_t seqno, uint32_t enable)
1130{
1131 if (enable)
1132 return avc_control(AVC_CALLBACK_AUDITALLOW_ENABLE,
1133 ssid, tsid, tclass, perms, seqno, 0);
1134 else
1135 return avc_control(AVC_CALLBACK_AUDITALLOW_DISABLE,
1136 ssid, tsid, tclass, perms, seqno, 0);
1137}
1138
1139/**
1140 * avc_ss_set_auditdeny - Enable or disable auditing of denied permissions.
1141 * @ssid: source security identifier or %SECSID_WILD
1142 * @tsid: target security identifier or %SECSID_WILD
1143 * @tclass: target security class
1144 * @perms: permissions to grant
1145 * @seqno: policy sequence number
1146 * @enable: enable flag.
1147 */
1148int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
1149 security_class_t tclass, access_vector_t perms,
1150 uint32_t seqno, uint32_t enable)
1151{
1152 if (enable)
1153 return avc_control(AVC_CALLBACK_AUDITDENY_ENABLE,
1154 ssid, tsid, tclass, perms, seqno, 0);
1155 else
1156 return avc_control(AVC_CALLBACK_AUDITDENY_DISABLE,
1157 ssid, tsid, tclass, perms, seqno, 0);
1158}