1use crate::access_cache::AccessCache;
6use crate::policy::{AccessDecision, XpermsAccessDecision, XpermsKind};
7use crate::security_server::SecurityServerBackend;
8use crate::{FsNodeClass, KernelClass, NullessByteStr, ObjectClass, SecurityId};
9use std::sync::Arc;
10
11pub use crate::access_cache::CacheStats;
12
13pub(super) trait Query {
20 fn compute_access_decision(
23 &self,
24 source_sid: SecurityId,
25 target_sid: SecurityId,
26 target_class: ObjectClass,
27 ) -> AccessDecision;
28
29 fn compute_new_fs_node_sid(
34 &self,
35 source_sid: SecurityId,
36 target_sid: SecurityId,
37 fs_node_class: FsNodeClass,
38 ) -> Result<SecurityId, anyhow::Error>;
39
40 fn compute_new_fs_node_sid_with_name(
45 &self,
46 source_sid: SecurityId,
47 target_sid: SecurityId,
48 fs_node_class: FsNodeClass,
49 fs_node_name: NullessByteStr<'_>,
50 ) -> Option<SecurityId>;
51
52 fn compute_xperms_access_decision(
56 &self,
57 xperms_kind: XpermsKind,
58 source_sid: SecurityId,
59 target_sid: SecurityId,
60 target_class: ObjectClass,
61 xperms_prefix: u8,
62 ) -> XpermsAccessDecision;
63}
64
65#[derive(Clone, Hash, PartialEq, Eq)]
66struct AccessQueryArgs {
67 source_sid: SecurityId,
68 target_sid: SecurityId,
69 target_class: ObjectClass,
70}
71
72#[derive(Clone, Hash, PartialEq, Eq)]
73struct XpermsAccessQueryArgs {
74 xperms_kind: XpermsKind,
75 source_sid: SecurityId,
76 target_sid: SecurityId,
77 target_class: ObjectClass,
78 xperms_prefix: u8,
79}
80
81pub(super) struct FifoQueryCache {
83 access_cache: AccessCache<AccessQueryArgs, AccessDecision>,
84 sid_cache: AccessCache<AccessQueryArgs, SecurityId>,
85 xperms_access_cache: AccessCache<XpermsAccessQueryArgs, XpermsAccessDecision>,
86}
87
88impl FifoQueryCache {
89 const XPERMS_CAPACITY_MULTIPLIER: f32 = 0.25;
91
92 pub fn new(capacity: usize) -> Self {
98 assert!(capacity > 0, "cannot instantiate fixed access vector cache of size 0");
99 let xperms_access_cache_capacity =
100 (Self::XPERMS_CAPACITY_MULTIPLIER * (capacity as f32)) as usize;
101 assert!(
102 xperms_access_cache_capacity > 0,
103 "cannot instantiate xperms cache partition of size 0"
104 );
105
106 Self {
107 access_cache: AccessCache::with_capacity(capacity),
108 sid_cache: AccessCache::with_capacity(capacity),
109 xperms_access_cache: AccessCache::with_capacity(xperms_access_cache_capacity),
110 }
111 }
112
113 pub fn cache_stats(&self) -> CacheStats {
114 let stats = &self.access_cache.cache_stats() + &self.sid_cache.cache_stats();
115 &stats + &self.xperms_access_cache.cache_stats()
116 }
117
118 pub fn compute_access_decision(
119 &self,
120 delegate: &impl Query,
121 source_sid: SecurityId,
122 target_sid: SecurityId,
123 target_class: ObjectClass,
124 ) -> AccessDecision {
125 let query_args =
126 AccessQueryArgs { source_sid, target_sid, target_class: target_class.clone() };
127 self.access_cache.get_or_insert(query_args, || {
128 delegate.compute_access_decision(source_sid, target_sid, target_class)
129 })
130 }
131
132 pub fn compute_new_fs_node_sid(
133 &self,
134 delegate: &impl Query,
135 source_sid: SecurityId,
136 target_sid: SecurityId,
137 fs_node_class: FsNodeClass,
138 ) -> Result<SecurityId, anyhow::Error> {
139 let target_class = ObjectClass::Kernel(KernelClass::from(fs_node_class));
140 let query_args = AccessQueryArgs { source_sid, target_sid, target_class };
141 self.sid_cache.get_or_try_insert(query_args, || {
142 delegate.compute_new_fs_node_sid(source_sid, target_sid, fs_node_class)
143 })
144 }
145
146 pub fn compute_new_fs_node_sid_with_name(
147 &self,
148 delegate: &impl Query,
149 source_sid: SecurityId,
150 target_sid: SecurityId,
151 fs_node_class: FsNodeClass,
152 fs_node_name: NullessByteStr<'_>,
153 ) -> Option<SecurityId> {
154 delegate.compute_new_fs_node_sid_with_name(
155 source_sid,
156 target_sid,
157 fs_node_class,
158 fs_node_name,
159 )
160 }
161
162 pub fn compute_xperms_access_decision(
163 &self,
164 delegate: &impl Query,
165 xperms_kind: XpermsKind,
166 source_sid: SecurityId,
167 target_sid: SecurityId,
168 target_class: ObjectClass,
169 xperms_prefix: u8,
170 ) -> XpermsAccessDecision {
171 let query_args = XpermsAccessQueryArgs {
172 xperms_kind: xperms_kind.clone(),
173 source_sid,
174 target_sid,
175 target_class: target_class.clone(),
176 xperms_prefix,
177 };
178 self.xperms_access_cache.get_or_insert(query_args, || {
179 delegate.compute_xperms_access_decision(
180 xperms_kind,
181 source_sid,
182 target_sid,
183 target_class,
184 xperms_prefix,
185 )
186 })
187 }
188
189 pub fn reset(&self) {
190 self.access_cache.reset();
191 self.sid_cache.reset();
192 self.xperms_access_cache.reset();
193 }
194
195 #[cfg(test)]
197 fn access_cache_is_full(&self) -> bool {
198 self.access_cache.is_full()
199 }
200
201 #[cfg(test)]
203 fn xperms_access_cache_is_full(&self) -> bool {
204 self.xperms_access_cache.is_full()
205 }
206}
207
208const DEFAULT_SHARED_SIZE: usize = 2000;
210
211#[derive(Clone)]
213pub(super) struct AccessVectorCache {
214 cache: Arc<FifoQueryCache>,
215 backend: Arc<SecurityServerBackend>,
216}
217
218impl AccessVectorCache {
219 pub fn new(backend: Arc<SecurityServerBackend>) -> Self {
220 let cache = FifoQueryCache::new(DEFAULT_SHARED_SIZE);
221 Self { cache: Arc::new(cache), backend }
222 }
223
224 pub fn cache_stats(&self) -> CacheStats {
225 self.cache.cache_stats()
226 }
227
228 pub fn reset(&self) {
229 self.cache.reset()
230 }
231}
232
233impl Query for AccessVectorCache {
234 fn compute_access_decision(
235 &self,
236 source_sid: SecurityId,
237 target_sid: SecurityId,
238 target_class: ObjectClass,
239 ) -> AccessDecision {
240 self.cache.compute_access_decision(
241 self.backend.as_ref(),
242 source_sid,
243 target_sid,
244 target_class,
245 )
246 }
247
248 fn compute_new_fs_node_sid(
249 &self,
250 source_sid: SecurityId,
251 target_sid: SecurityId,
252 fs_node_class: FsNodeClass,
253 ) -> Result<SecurityId, anyhow::Error> {
254 self.cache.compute_new_fs_node_sid(
255 self.backend.as_ref(),
256 source_sid,
257 target_sid,
258 fs_node_class,
259 )
260 }
261
262 fn compute_new_fs_node_sid_with_name(
263 &self,
264 source_sid: SecurityId,
265 target_sid: SecurityId,
266 fs_node_class: FsNodeClass,
267 fs_node_name: NullessByteStr<'_>,
268 ) -> Option<SecurityId> {
269 self.cache.compute_new_fs_node_sid_with_name(
270 self.backend.as_ref(),
271 source_sid,
272 target_sid,
273 fs_node_class,
274 fs_node_name,
275 )
276 }
277
278 fn compute_xperms_access_decision(
279 &self,
280 xperms_kind: XpermsKind,
281 source_sid: SecurityId,
282 target_sid: SecurityId,
283 target_class: ObjectClass,
284 xperms_prefix: u8,
285 ) -> XpermsAccessDecision {
286 self.cache.compute_xperms_access_decision(
287 self.backend.as_ref(),
288 xperms_kind,
289 source_sid,
290 target_sid,
291 target_class,
292 xperms_prefix,
293 )
294 }
295}
296
297#[cfg(test)]
299mod testing {
300 use crate::SecurityId;
301
302 use std::num::NonZeroU32;
303 use std::sync::LazyLock;
304 use std::sync::atomic::{AtomicU32, Ordering};
305
306 pub(super) static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
308
309 pub(super) const TEST_CAPACITY: usize = 10;
311
312 pub(super) fn unique_sid() -> SecurityId {
314 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
315 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
316 }
317
318 pub(super) fn unique_sids(count: usize) -> Vec<SecurityId> {
320 (0..count).map(|_| unique_sid()).collect()
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::testing::*;
327 use super::*;
328 use crate::KernelClass;
329 use crate::policy::{AccessVector, XpermsBitmap};
330
331 use std::sync::atomic::{AtomicUsize, Ordering};
332
333 #[derive(Default)]
335 struct TestDelegate {
336 query_count: AtomicUsize,
337 }
338
339 impl TestDelegate {
340 fn query_count(&self) -> usize {
341 self.query_count.load(Ordering::Relaxed)
342 }
343 }
344
345 impl Query for TestDelegate {
346 fn compute_access_decision(
347 &self,
348 _source_sid: SecurityId,
349 _target_sid: SecurityId,
350 _target_class: ObjectClass,
351 ) -> AccessDecision {
352 self.query_count.fetch_add(1, Ordering::Relaxed);
353 AccessDecision::allow(AccessVector::ALL)
354 }
355
356 fn compute_new_fs_node_sid(
357 &self,
358 _source_sid: SecurityId,
359 _target_sid: SecurityId,
360 _fs_node_class: FsNodeClass,
361 ) -> Result<SecurityId, anyhow::Error> {
362 unreachable!()
363 }
364
365 fn compute_new_fs_node_sid_with_name(
366 &self,
367 _source_sid: SecurityId,
368 _target_sid: SecurityId,
369 _fs_node_class: FsNodeClass,
370 _fs_node_name: NullessByteStr<'_>,
371 ) -> Option<SecurityId> {
372 unreachable!()
373 }
374
375 fn compute_xperms_access_decision(
376 &self,
377 _xperms_kind: XpermsKind,
378 _source_sid: SecurityId,
379 _target_sid: SecurityId,
380 _target_class: ObjectClass,
381 _xperms_prefix: u8,
382 ) -> XpermsAccessDecision {
383 self.query_count.fetch_add(1, Ordering::Relaxed);
384 XpermsAccessDecision::ALLOW_ALL
385 }
386 }
387
388 #[test]
389 fn fixed_access_vector_cache_add_entry() {
390 let delegate = TestDelegate::default();
391 let avc = FifoQueryCache::new(TEST_CAPACITY);
392 assert_eq!(0, delegate.query_count());
393 assert_eq!(
394 AccessVector::ALL,
395 avc.compute_access_decision(
396 &delegate,
397 A_TEST_SID.clone(),
398 A_TEST_SID.clone(),
399 KernelClass::Process.into()
400 )
401 .allow
402 );
403 assert_eq!(1, delegate.query_count());
404 assert_eq!(
405 AccessVector::ALL,
406 avc.compute_access_decision(
407 &delegate,
408 A_TEST_SID.clone(),
409 A_TEST_SID.clone(),
410 KernelClass::Process.into()
411 )
412 .allow
413 );
414 assert_eq!(1, delegate.query_count());
415 assert_eq!(false, avc.access_cache_is_full());
416 }
417
418 #[test]
419 fn fixed_access_vector_cache_reset() {
420 let delegate = TestDelegate::default();
421 let avc = FifoQueryCache::new(TEST_CAPACITY);
422
423 avc.reset();
424 assert_eq!(false, avc.access_cache_is_full());
425
426 assert_eq!(0, delegate.query_count());
427 assert_eq!(
428 AccessVector::ALL,
429 avc.compute_access_decision(
430 &delegate,
431 A_TEST_SID.clone(),
432 A_TEST_SID.clone(),
433 KernelClass::Process.into()
434 )
435 .allow
436 );
437 assert_eq!(1, delegate.query_count());
438 assert_eq!(false, avc.access_cache_is_full());
439
440 avc.reset();
441 assert_eq!(false, avc.access_cache_is_full());
442 }
443
444 #[test]
445 fn fixed_access_vector_cache_fill() {
446 let delegate = TestDelegate::default();
447 let avc = FifoQueryCache::new(TEST_CAPACITY);
448
449 for sid in unique_sids(avc.access_cache.capacity()) {
450 avc.compute_access_decision(
451 &delegate,
452 sid,
453 A_TEST_SID.clone(),
454 KernelClass::Process.into(),
455 );
456 }
457 assert_eq!(true, avc.access_cache_is_full());
458
459 avc.reset();
460 assert_eq!(false, avc.access_cache_is_full());
461
462 for sid in unique_sids(avc.access_cache.capacity()) {
463 avc.compute_access_decision(
464 &delegate,
465 A_TEST_SID.clone(),
466 sid,
467 KernelClass::Process.into(),
468 );
469 }
470 assert_eq!(true, avc.access_cache_is_full());
471
472 avc.reset();
473 assert_eq!(false, avc.access_cache_is_full());
474 }
475
476 #[test]
477 fn fixed_access_vector_cache_full_miss() {
478 let delegate = TestDelegate::default();
479 let avc = FifoQueryCache::new(TEST_CAPACITY);
480
481 avc.compute_access_decision(
483 &delegate,
484 A_TEST_SID.clone(),
485 A_TEST_SID.clone(),
486 KernelClass::Process.into(),
487 );
488 assert!(!avc.access_cache_is_full());
489
490 for sid in unique_sids(avc.access_cache.capacity()) {
492 avc.compute_access_decision(
493 &delegate,
494 sid,
495 A_TEST_SID.clone(),
496 KernelClass::Process.into(),
497 );
498 }
499 assert!(avc.access_cache_is_full());
500
501 let delegate_query_count = delegate.query_count();
503 avc.compute_access_decision(
504 &delegate,
505 A_TEST_SID.clone(),
506 A_TEST_SID.clone(),
507 KernelClass::Process.into(),
508 );
509 assert_eq!(delegate_query_count + 1, delegate.query_count());
510
511 for sid in unique_sids(avc.access_cache.capacity()) {
516 avc.compute_access_decision(
517 &delegate,
518 A_TEST_SID.clone(),
519 A_TEST_SID.clone(),
520 KernelClass::Process.into(),
521 );
522 avc.compute_access_decision(
523 &delegate,
524 sid,
525 A_TEST_SID.clone(),
526 KernelClass::Process.into(),
527 );
528 }
529
530 let delegate_query_count = delegate.query_count();
532 avc.compute_access_decision(
533 &delegate,
534 A_TEST_SID.clone(),
535 A_TEST_SID.clone(),
536 KernelClass::Process.into(),
537 );
538 assert_eq!(delegate_query_count + 1, delegate.query_count());
539 }
540
541 #[test]
542 fn access_vector_cache_ioctl_hit() {
543 let delegate = TestDelegate::default();
544 let avc = FifoQueryCache::new(TEST_CAPACITY);
545 assert_eq!(0, delegate.query_count());
546 assert_eq!(
547 XpermsBitmap::ALL,
548 avc.compute_xperms_access_decision(
549 &delegate,
550 XpermsKind::Ioctl,
551 A_TEST_SID.clone(),
552 A_TEST_SID.clone(),
553 KernelClass::Process.into(),
554 0x0,
555 )
556 .allow
557 );
558 assert_eq!(1, delegate.query_count());
559 assert_eq!(
561 XpermsBitmap::ALL,
562 avc.compute_xperms_access_decision(
563 &delegate,
564 XpermsKind::Ioctl,
565 A_TEST_SID.clone(),
566 A_TEST_SID.clone(),
567 KernelClass::Process.into(),
568 0x0
569 )
570 .allow
571 );
572 assert_eq!(1, delegate.query_count());
573 }
574
575 #[test]
576 fn access_vector_cache_ioctl_miss() {
577 let delegate = TestDelegate::default();
578 let avc = FifoQueryCache::new(TEST_CAPACITY);
579
580 avc.compute_xperms_access_decision(
582 &delegate,
583 XpermsKind::Ioctl,
584 A_TEST_SID.clone(),
585 A_TEST_SID.clone(),
586 KernelClass::Process.into(),
587 0x0,
588 );
589
590 for ioctl_prefix in 0x1..(1 + avc.xperms_access_cache.capacity())
592 .try_into()
593 .expect("assumed that test xperms cache capacity was < 255")
594 {
595 avc.compute_xperms_access_decision(
596 &delegate,
597 XpermsKind::Ioctl,
598 A_TEST_SID.clone(),
599 A_TEST_SID.clone(),
600 KernelClass::Process.into(),
601 ioctl_prefix,
602 );
603 }
604 assert!(delegate.query_count() > 1);
607 assert!(avc.xperms_access_cache_is_full());
608 let delegate_query_count = delegate.query_count();
609
610 avc.compute_xperms_access_decision(
612 &delegate,
613 XpermsKind::Ioctl,
614 A_TEST_SID.clone(),
615 A_TEST_SID.clone(),
616 KernelClass::Process.into(),
617 0x0,
618 );
619 assert_eq!(delegate_query_count + 1, delegate.query_count());
620 }
621
622 #[test]
623 fn access_vector_cache_nlmsg_hit() {
624 let delegate = TestDelegate::default();
625 let avc = FifoQueryCache::new(TEST_CAPACITY);
626 assert_eq!(0, delegate.query_count());
627 assert_eq!(
628 XpermsBitmap::ALL,
629 avc.compute_xperms_access_decision(
630 &delegate,
631 XpermsKind::Nlmsg,
632 A_TEST_SID.clone(),
633 A_TEST_SID.clone(),
634 KernelClass::Process.into(),
635 0x0,
636 )
637 .allow
638 );
639 assert_eq!(1, delegate.query_count());
640 assert_eq!(
642 XpermsBitmap::ALL,
643 avc.compute_xperms_access_decision(
644 &delegate,
645 XpermsKind::Nlmsg,
646 A_TEST_SID.clone(),
647 A_TEST_SID.clone(),
648 KernelClass::Process.into(),
649 0x0
650 )
651 .allow
652 );
653 assert_eq!(1, delegate.query_count());
654 }
655
656 #[test]
657 fn access_vector_cache_nlmsg_miss() {
658 let delegate = TestDelegate::default();
659 let avc = FifoQueryCache::new(TEST_CAPACITY);
660
661 avc.compute_xperms_access_decision(
663 &delegate,
664 XpermsKind::Nlmsg,
665 A_TEST_SID.clone(),
666 A_TEST_SID.clone(),
667 KernelClass::Process.into(),
668 0x0,
669 );
670
671 for nlmsg_prefix in 0x1..(1 + avc.xperms_access_cache.capacity())
673 .try_into()
674 .expect("assumed that test xperms cache capacity was < 255")
675 {
676 avc.compute_xperms_access_decision(
677 &delegate,
678 XpermsKind::Nlmsg,
679 A_TEST_SID.clone(),
680 A_TEST_SID.clone(),
681 KernelClass::Process.into(),
682 nlmsg_prefix,
683 );
684 }
685 assert!(delegate.query_count() > 1);
688 assert!(avc.xperms_access_cache_is_full());
689 let delegate_query_count = delegate.query_count();
690
691 avc.compute_xperms_access_decision(
693 &delegate,
694 XpermsKind::Nlmsg,
695 A_TEST_SID.clone(),
696 A_TEST_SID.clone(),
697 KernelClass::Process.into(),
698 0x0,
699 );
700 assert_eq!(delegate_query_count + 1, delegate.query_count());
701 }
702
703 #[test]
704 fn access_vector_cache_nlmsg_and_ioctl() {
705 let delegate = TestDelegate::default();
706 let avc = FifoQueryCache::new(TEST_CAPACITY);
707
708 avc.compute_xperms_access_decision(
710 &delegate,
711 XpermsKind::Ioctl,
712 A_TEST_SID.clone(),
713 A_TEST_SID.clone(),
714 KernelClass::Process.into(),
715 0x0,
716 );
717 assert_eq!(avc.cache_stats().allocs, 1);
718
719 avc.compute_xperms_access_decision(
722 &delegate,
723 XpermsKind::Nlmsg,
724 A_TEST_SID.clone(),
725 A_TEST_SID.clone(),
726 KernelClass::Process.into(),
727 0x0,
728 );
729 assert_eq!(avc.cache_stats().allocs, 2);
730 }
731}