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