blob: b1ec57fb529a5c685289b997ea27c3a6da6b7a9b [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
Stephen Smalley9eb9c932014-02-19 09:16:17 -050070int avc_context_to_sid_raw(const char * 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
Stephen Smalley9eb9c932014-02-19 09:16:17 -050082int avc_context_to_sid(const char * ctx, security_id_t * sid)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040083{
84 int ret;
Stephen Smalley9eb9c932014-02-19 09:16:17 -050085 char * rctx;
Joshua Brindle13cd4c82008-08-19 15:30:36 -040086
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
Stephen Smalley9eb9c932014-02-19 09:16:17 -050097int avc_sid_to_context_raw(security_id_t sid, char ** ctx)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040098{
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
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500108int avc_sid_to_context(security_id_t sid, char ** ctx)
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400109{
110 int ret;
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500111 char * rctx;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400112
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;
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500136 char * con;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400137
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
Petr Lautrbach417cb8d2015-03-13 15:54:09 +0100291 avc_log(SELINUX_INFO, "%s: %u 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{
Stephen Smalleydcd81672013-12-30 14:38:11 -0500339 memset(ae, 0, sizeof(*ae));
Eamon Walsh72394802009-03-11 19:01:42 -0400340}
341
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400342static inline struct avc_node *avc_claim_node(security_id_t ssid,
343 security_id_t tsid,
344 security_class_t tclass)
345{
346 struct avc_node *new;
347 int hvalue;
348
349 if (!avc_node_freelist)
350 avc_cleanup();
351
352 if (avc_node_freelist) {
353 new = avc_node_freelist;
354 avc_node_freelist = avc_node_freelist->next;
355 avc_cache.active_nodes++;
356 } else {
357 new = avc_reclaim_node();
358 if (!new)
359 goto out;
360 }
361
362 hvalue = avc_hash(ssid, tsid, tclass);
Eamon Walsh72394802009-03-11 19:01:42 -0400363 avc_clear_avc_entry(&new->ae);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400364 new->ae.used = 1;
365 new->ae.ssid = ssid;
366 new->ae.tsid = tsid;
367 new->ae.tclass = tclass;
368 new->next = avc_cache.slots[hvalue];
369 avc_cache.slots[hvalue] = new;
370
371 out:
372 return new;
373}
374
375static inline struct avc_node *avc_search_node(security_id_t ssid,
376 security_id_t tsid,
377 security_class_t tclass,
378 int *probes)
379{
380 struct avc_node *cur;
381 int hvalue;
382 int tprobes = 1;
383
384 hvalue = avc_hash(ssid, tsid, tclass);
385 cur = avc_cache.slots[hvalue];
386 while (cur != NULL &&
387 (ssid != cur->ae.ssid ||
388 tclass != cur->ae.tclass || tsid != cur->ae.tsid)) {
389 tprobes++;
390 cur = cur->next;
391 }
392
393 if (cur == NULL) {
394 /* cache miss */
395 goto out;
396 }
397
398 /* cache hit */
399 if (probes)
400 *probes = tprobes;
401
402 cur->ae.used = 1;
403
404 out:
405 return cur;
406}
407
408/**
409 * avc_lookup - Look up an AVC entry.
410 * @ssid: source security identifier
411 * @tsid: target security identifier
412 * @tclass: target security class
413 * @requested: requested permissions, interpreted based on @tclass
414 * @aeref: AVC entry reference
415 *
416 * Look up an AVC entry that is valid for the
417 * @requested permissions between the SID pair
418 * (@ssid, @tsid), interpreting the permissions
419 * based on @tclass. If a valid AVC entry exists,
420 * then this function updates @aeref to refer to the
421 * entry and returns %0. Otherwise, -1 is returned.
422 */
423static int avc_lookup(security_id_t ssid, security_id_t tsid,
424 security_class_t tclass,
425 access_vector_t requested, struct avc_entry_ref *aeref)
426{
427 struct avc_node *node;
428 int probes, rc = 0;
429
430 avc_cache_stats_incr(cav_lookups);
431 node = avc_search_node(ssid, tsid, tclass, &probes);
432
433 if (node && ((node->ae.avd.decided & requested) == requested)) {
434 avc_cache_stats_incr(cav_hits);
435 avc_cache_stats_add(cav_probes, probes);
436 aeref->ae = &node->ae;
437 goto out;
438 }
439
440 avc_cache_stats_incr(cav_misses);
441 rc = -1;
442 out:
443 return rc;
444}
445
446/**
447 * avc_insert - Insert an AVC entry.
448 * @ssid: source security identifier
449 * @tsid: target security identifier
450 * @tclass: target security class
451 * @ae: AVC entry
452 * @aeref: AVC entry reference
453 *
454 * Insert an AVC entry for the SID pair
455 * (@ssid, @tsid) and class @tclass.
456 * The access vectors and the sequence number are
457 * normally provided by the security server in
458 * response to a security_compute_av() call. If the
459 * sequence number @ae->avd.seqno is not less than the latest
460 * revocation notification, then the function copies
461 * the access vectors into a cache entry, updates
462 * @aeref to refer to the entry, and returns %0.
463 * Otherwise, this function returns -%1 with @errno set to %EAGAIN.
464 */
465static int avc_insert(security_id_t ssid, security_id_t tsid,
466 security_class_t tclass,
467 struct avc_entry *ae, struct avc_entry_ref *aeref)
468{
469 struct avc_node *node;
470 int rc = 0;
471
472 if (ae->avd.seqno < avc_cache.latest_notif) {
Eamon Walsheee0f022008-10-31 10:20:33 -0400473 avc_log(SELINUX_WARNING,
Petr Lautrbach417cb8d2015-03-13 15:54:09 +0100474 "%s: seqno %u < latest_notif %u\n", avc_prefix,
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400475 ae->avd.seqno, avc_cache.latest_notif);
476 errno = EAGAIN;
477 rc = -1;
478 goto out;
479 }
480
481 node = avc_claim_node(ssid, tsid, tclass);
482 if (!node) {
483 rc = -1;
484 goto out;
485 }
486
Stephen Smalleydcd81672013-12-30 14:38:11 -0500487 memcpy(&node->ae.avd, &ae->avd, sizeof(ae->avd));
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400488 aeref->ae = &node->ae;
489 out:
490 return rc;
491}
492
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400493void avc_cleanup(void)
494{
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400495}
496
497hidden_def(avc_cleanup)
498
499int avc_reset(void)
500{
501 struct avc_callback_node *c;
502 int i, ret, rc = 0, errsave = 0;
503 struct avc_node *node, *tmp;
504 errno = 0;
505
506 if (!avc_running)
507 return 0;
508
509 avc_get_lock(avc_lock);
510
511 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
512 node = avc_cache.slots[i];
513 while (node) {
514 tmp = node;
515 node = node->next;
Eamon Walsh72394802009-03-11 19:01:42 -0400516 avc_clear_avc_entry(&tmp->ae);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400517 tmp->next = avc_node_freelist;
518 avc_node_freelist = tmp;
519 avc_cache.active_nodes--;
520 }
521 avc_cache.slots[i] = 0;
522 }
523 avc_cache.lru_hint = 0;
524
525 avc_release_lock(avc_lock);
526
527 memset(&cache_stats, 0, sizeof(cache_stats));
528
529 for (c = avc_callbacks; c; c = c->next) {
530 if (c->events & AVC_CALLBACK_RESET) {
531 ret = c->callback(AVC_CALLBACK_RESET, 0, 0, 0, 0, 0);
532 if (ret && !rc) {
533 rc = ret;
534 errsave = errno;
535 }
536 }
537 }
538 errno = errsave;
539 return rc;
540}
541
542hidden_def(avc_reset)
543
544void avc_destroy(void)
545{
546 struct avc_callback_node *c;
547 struct avc_node *node, *tmp;
548 int i;
Dan Walshed5dc692012-03-06 10:44:30 -0500549 /* avc_init needs to be called before this function */
550 assert(avc_running);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400551
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
Stephen Smalley8b114a32013-10-28 16:52:50 -0400734
735static void avd_init(struct av_decision *avd)
736{
737 avd->allowed = 0;
738 avd->auditallow = 0;
739 avd->auditdeny = 0xffffffff;
740 avd->seqno = avc_cache.latest_notif;
741 avd->flags = 0;
742}
743
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400744int avc_has_perm_noaudit(security_id_t ssid,
745 security_id_t tsid,
746 security_class_t tclass,
747 access_vector_t requested,
748 struct avc_entry_ref *aeref, struct av_decision *avd)
749{
750 struct avc_entry *ae;
751 int rc = 0;
752 struct avc_entry entry;
753 access_vector_t denied;
754 struct avc_entry_ref ref;
755
Stephen Smalley8b114a32013-10-28 16:52:50 -0400756 if (avd)
757 avd_init(avd);
758
Eamon Walsh7ab6b292009-03-10 20:31:38 -0400759 if (!avc_using_threads && !avc_app_main_loop) {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400760 (void)avc_netlink_check_nb();
761 }
762
763 if (!aeref) {
764 avc_entry_ref_init(&ref);
765 aeref = &ref;
766 }
767
768 avc_get_lock(avc_lock);
769 avc_cache_stats_incr(entry_lookups);
770 ae = aeref->ae;
771 if (ae) {
772 if (ae->ssid == ssid &&
773 ae->tsid == tsid &&
774 ae->tclass == tclass &&
775 ((ae->avd.decided & requested) == requested)) {
776 avc_cache_stats_incr(entry_hits);
777 ae->used = 1;
778 } else {
779 avc_cache_stats_incr(entry_discards);
780 ae = 0;
781 }
782 }
783
784 if (!ae) {
785 avc_cache_stats_incr(entry_misses);
786 rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
787 if (rc) {
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400788 rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
789 tclass, requested,
790 &entry.avd);
Stephen Smalley8b114a32013-10-28 16:52:50 -0400791 if (rc && errno == EINVAL && !avc_enforcing) {
792 rc = errno = 0;
793 goto out;
794 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400795 if (rc)
796 goto out;
797 rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
798 if (rc)
799 goto out;
800 }
801 ae = aeref->ae;
802 }
803
804 if (avd)
805 memcpy(avd, &ae->avd, sizeof(*avd));
806
807 denied = requested & ~(ae->avd.allowed);
808
809 if (!requested || denied) {
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400810 if (!avc_enforcing ||
811 (ae->avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE))
812 ae->avd.allowed |= requested;
813 else {
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400814 errno = EACCES;
815 rc = -1;
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400816 }
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400817 }
818
819 out:
820 avc_release_lock(avc_lock);
821 return rc;
822}
823
824hidden_def(avc_has_perm_noaudit)
825
826int avc_has_perm(security_id_t ssid, security_id_t tsid,
827 security_class_t tclass, access_vector_t requested,
828 struct avc_entry_ref *aeref, void *auditdata)
829{
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400830 struct av_decision avd;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400831 int errsave, rc;
832
833 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
834 errsave = errno;
835 avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
836 errno = errsave;
837 return rc;
838}
839
840int avc_compute_create(security_id_t ssid, security_id_t tsid,
841 security_class_t tclass, security_id_t *newsid)
842{
843 int rc;
Eamon Walsh72394802009-03-11 19:01:42 -0400844 struct avc_entry_ref aeref;
845 struct avc_entry entry;
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500846 char * ctx;
Eamon Walsh72394802009-03-11 19:01:42 -0400847
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400848 *newsid = NULL;
Eamon Walsh72394802009-03-11 19:01:42 -0400849 avc_entry_ref_init(&aeref);
850
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400851 avc_get_lock(avc_lock);
Eamon Walsh72394802009-03-11 19:01:42 -0400852
853 /* check for a cached entry */
854 rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
855 if (rc) {
856 /* need to make a cache entry for this tuple */
KaiGai Kohei55ed6e72009-04-07 22:10:30 -0400857 rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
858 tclass, 0, &entry.avd);
Eamon Walsh72394802009-03-11 19:01:42 -0400859 if (rc)
860 goto out;
861 rc = avc_insert(ssid, tsid, tclass, &entry, &aeref);
862 if (rc)
863 goto out;
864 }
865
866 /* check for a saved compute_create value */
867 if (!aeref.ae->create_sid) {
868 /* need to query the kernel policy */
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400869 rc = security_compute_create_raw(ssid->ctx, tsid->ctx, tclass,
870 &ctx);
871 if (rc)
872 goto out;
873 rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400874 freecon(ctx);
Eamon Walsh72394802009-03-11 19:01:42 -0400875 if (rc)
876 goto out;
877
878 aeref.ae->create_sid = *newsid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400879 } else {
Eamon Walsh72394802009-03-11 19:01:42 -0400880 /* found saved value */
881 *newsid = aeref.ae->create_sid;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400882 }
Eamon Walsh72394802009-03-11 19:01:42 -0400883
884 rc = 0;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400885out:
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400886 avc_release_lock(avc_lock);
887 return rc;
888}
889
890int avc_compute_member(security_id_t ssid, security_id_t tsid,
891 security_class_t tclass, security_id_t *newsid)
892{
893 int rc;
Stephen Smalley9eb9c932014-02-19 09:16:17 -0500894 char * ctx = NULL;
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400895 *newsid = NULL;
Dan Walshed5dc692012-03-06 10:44:30 -0500896 /* avc_init needs to be called before this function */
897 assert(avc_running);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400898 avc_get_lock(avc_lock);
Eamon Walsh58866dd2009-09-02 17:41:22 -0400899
900 rc = security_compute_member_raw(ssid->ctx, tsid->ctx, tclass, &ctx);
901 if (rc)
902 goto out;
903 rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
904 freecon(ctx);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400905out:
906 avc_release_lock(avc_lock);
907 return rc;
908}
909
910int avc_add_callback(int (*callback) (uint32_t event, security_id_t ssid,
911 security_id_t tsid,
912 security_class_t tclass,
913 access_vector_t perms,
914 access_vector_t * out_retained),
915 uint32_t events, security_id_t ssid,
916 security_id_t tsid,
917 security_class_t tclass, access_vector_t perms)
918{
919 struct avc_callback_node *c;
920 int rc = 0;
921
922 c = avc_malloc(sizeof(*c));
923 if (!c) {
924 rc = -1;
925 goto out;
926 }
927
928 c->callback = callback;
929 c->events = events;
930 c->ssid = ssid;
931 c->tsid = tsid;
932 c->tclass = tclass;
933 c->perms = perms;
934 c->next = avc_callbacks;
935 avc_callbacks = c;
936 out:
937 return rc;
938}
939
940static inline int avc_sidcmp(security_id_t x, security_id_t y)
941{
942 return (x == y || x == SECSID_WILD || y == SECSID_WILD);
943}
944
945static inline void avc_update_node(uint32_t event, struct avc_node *node,
946 access_vector_t perms)
947{
948 switch (event) {
949 case AVC_CALLBACK_GRANT:
950 node->ae.avd.allowed |= perms;
951 break;
952 case AVC_CALLBACK_TRY_REVOKE:
953 case AVC_CALLBACK_REVOKE:
954 node->ae.avd.allowed &= ~perms;
955 break;
956 case AVC_CALLBACK_AUDITALLOW_ENABLE:
957 node->ae.avd.auditallow |= perms;
958 break;
959 case AVC_CALLBACK_AUDITALLOW_DISABLE:
960 node->ae.avd.auditallow &= ~perms;
961 break;
962 case AVC_CALLBACK_AUDITDENY_ENABLE:
963 node->ae.avd.auditdeny |= perms;
964 break;
965 case AVC_CALLBACK_AUDITDENY_DISABLE:
966 node->ae.avd.auditdeny &= ~perms;
967 break;
968 }
969}
970
971static int avc_update_cache(uint32_t event, security_id_t ssid,
972 security_id_t tsid, security_class_t tclass,
973 access_vector_t perms)
974{
975 struct avc_node *node;
976 int i;
977
978 avc_get_lock(avc_lock);
979
980 if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
981 /* apply to all matching nodes */
982 for (i = 0; i < AVC_CACHE_SLOTS; i++) {
983 for (node = avc_cache.slots[i]; node; node = node->next) {
984 if (avc_sidcmp(ssid, node->ae.ssid) &&
985 avc_sidcmp(tsid, node->ae.tsid) &&
986 tclass == node->ae.tclass) {
987 avc_update_node(event, node, perms);
988 }
989 }
990 }
991 } else {
992 /* apply to one node */
993 node = avc_search_node(ssid, tsid, tclass, 0);
994 if (node) {
995 avc_update_node(event, node, perms);
996 }
997 }
998
999 avc_release_lock(avc_lock);
1000
1001 return 0;
1002}
1003
1004/* avc_control - update cache and call callbacks
1005 *
1006 * This should not be called directly; use the individual event
1007 * functions instead.
1008 */
1009static int avc_control(uint32_t event, security_id_t ssid,
1010 security_id_t tsid, security_class_t tclass,
1011 access_vector_t perms,
1012 uint32_t seqno, access_vector_t * out_retained)
1013{
1014 struct avc_callback_node *c;
1015 access_vector_t tretained = 0, cretained = 0;
1016 int ret, rc = 0, errsave = 0;
1017 errno = 0;
1018
1019 /*
1020 * try_revoke only removes permissions from the cache
1021 * state if they are not retained by the object manager.
1022 * Hence, try_revoke must wait until after the callbacks have
1023 * been invoked to update the cache state.
1024 */
1025 if (event != AVC_CALLBACK_TRY_REVOKE)
1026 avc_update_cache(event, ssid, tsid, tclass, perms);
1027
1028 for (c = avc_callbacks; c; c = c->next) {
1029 if ((c->events & event) &&
1030 avc_sidcmp(c->ssid, ssid) &&
1031 avc_sidcmp(c->tsid, tsid) &&
1032 c->tclass == tclass && (c->perms & perms)) {
1033 cretained = 0;
1034 ret = c->callback(event, ssid, tsid, tclass,
1035 (c->perms & perms), &cretained);
1036 if (ret && !rc) {
1037 rc = ret;
1038 errsave = errno;
1039 }
1040 if (!ret)
1041 tretained |= cretained;
1042 }
1043 }
1044
1045 if (event == AVC_CALLBACK_TRY_REVOKE) {
1046 /* revoke any unretained permissions */
1047 perms &= ~tretained;
1048 avc_update_cache(event, ssid, tsid, tclass, perms);
1049 *out_retained = tretained;
1050 }
1051
1052 avc_get_lock(avc_lock);
1053 if (seqno > avc_cache.latest_notif)
1054 avc_cache.latest_notif = seqno;
1055 avc_release_lock(avc_lock);
1056
1057 errno = errsave;
1058 return rc;
1059}
1060
1061/**
1062 * avc_ss_grant - Grant previously denied permissions.
1063 * @ssid: source security identifier or %SECSID_WILD
1064 * @tsid: target security identifier or %SECSID_WILD
1065 * @tclass: target security class
1066 * @perms: permissions to grant
1067 * @seqno: policy sequence number
1068 */
1069int avc_ss_grant(security_id_t ssid, security_id_t tsid,
1070 security_class_t tclass, access_vector_t perms,
1071 uint32_t seqno)
1072{
1073 return avc_control(AVC_CALLBACK_GRANT,
1074 ssid, tsid, tclass, perms, seqno, 0);
1075}
1076
1077/**
1078 * avc_ss_try_revoke - Try to revoke previously granted permissions.
1079 * @ssid: source security identifier or %SECSID_WILD
1080 * @tsid: target security identifier or %SECSID_WILD
1081 * @tclass: target security class
1082 * @perms: permissions to grant
1083 * @seqno: policy sequence number
1084 * @out_retained: subset of @perms that are retained
1085 *
1086 * Try to revoke previously granted permissions, but
1087 * only if they are not retained as migrated permissions.
1088 * Return the subset of permissions that are retained via @out_retained.
1089 */
1090int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
1091 security_class_t tclass,
1092 access_vector_t perms, uint32_t seqno,
1093 access_vector_t * out_retained)
1094{
1095 return avc_control(AVC_CALLBACK_TRY_REVOKE,
1096 ssid, tsid, tclass, perms, seqno, out_retained);
1097}
1098
1099/**
1100 * avc_ss_revoke - Revoke previously granted permissions.
1101 * @ssid: source security identifier or %SECSID_WILD
1102 * @tsid: target security identifier or %SECSID_WILD
1103 * @tclass: target security class
1104 * @perms: permissions to grant
1105 * @seqno: policy sequence number
1106 *
1107 * Revoke previously granted permissions, even if
1108 * they are retained as migrated permissions.
1109 */
1110int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
1111 security_class_t tclass, access_vector_t perms,
1112 uint32_t seqno)
1113{
1114 return avc_control(AVC_CALLBACK_REVOKE,
1115 ssid, tsid, tclass, perms, seqno, 0);
1116}
1117
1118/**
1119 * avc_ss_reset - Flush the cache and revalidate migrated permissions.
1120 * @seqno: policy sequence number
1121 */
1122int avc_ss_reset(uint32_t seqno)
1123{
1124 int rc;
1125
1126 rc = avc_reset();
1127
1128 avc_get_lock(avc_lock);
1129 if (seqno > avc_cache.latest_notif)
1130 avc_cache.latest_notif = seqno;
1131 avc_release_lock(avc_lock);
1132
1133 return rc;
1134}
1135
1136/**
1137 * avc_ss_set_auditallow - Enable or disable auditing of granted permissions.
1138 * @ssid: source security identifier or %SECSID_WILD
1139 * @tsid: target security identifier or %SECSID_WILD
1140 * @tclass: target security class
1141 * @perms: permissions to grant
1142 * @seqno: policy sequence number
1143 * @enable: enable flag.
1144 */
1145int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
1146 security_class_t tclass, access_vector_t perms,
1147 uint32_t seqno, uint32_t enable)
1148{
1149 if (enable)
1150 return avc_control(AVC_CALLBACK_AUDITALLOW_ENABLE,
1151 ssid, tsid, tclass, perms, seqno, 0);
1152 else
1153 return avc_control(AVC_CALLBACK_AUDITALLOW_DISABLE,
1154 ssid, tsid, tclass, perms, seqno, 0);
1155}
1156
1157/**
1158 * avc_ss_set_auditdeny - Enable or disable auditing of denied permissions.
1159 * @ssid: source security identifier or %SECSID_WILD
1160 * @tsid: target security identifier or %SECSID_WILD
1161 * @tclass: target security class
1162 * @perms: permissions to grant
1163 * @seqno: policy sequence number
1164 * @enable: enable flag.
1165 */
1166int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
1167 security_class_t tclass, access_vector_t perms,
1168 uint32_t seqno, uint32_t enable)
1169{
1170 if (enable)
1171 return avc_control(AVC_CALLBACK_AUDITDENY_ENABLE,
1172 ssid, tsid, tclass, perms, seqno, 0);
1173 else
1174 return avc_control(AVC_CALLBACK_AUDITDENY_DISABLE,
1175 ssid, tsid, tclass, perms, seqno, 0);
1176}