1mod cache_stats;
6mod concurrent_access_cache;
7mod concurrent_cache;
8mod local_cache;
9
10use crate::SecurityId;
11use crate::kernel::{
12 KernelAccessDecision, KernelClass, KernelPermission, KernelXpermsAccessDecision,
13};
14use crate::policy::XpermsKind;
15use crate::security_server::SecurityServerBackend;
16use concurrent_access_cache::{ConcurrentSidCache, ConcurrentXpermsCache};
17use std::sync::Arc;
18
19pub use cache_stats::CacheStats;
20pub use concurrent_access_cache::{AccessCacheStorage, ConcurrentAccessCache};
21pub use local_cache::PerThreadCache;
22
23pub(crate) trait Query {
30 fn compute_access_decision(
33 &self,
34 source_sid: SecurityId,
35 target_sid: SecurityId,
36 target_class: KernelClass,
37 ) -> KernelAccessDecision;
38
39 fn compute_create_sid(
44 &self,
45 source_sid: SecurityId,
46 target_sid: SecurityId,
47 target_class: KernelClass,
48 name: &[u8],
49 ) -> Result<SecurityId, anyhow::Error>;
50
51 fn compute_xperms_access_decision(
55 &self,
56 xperms_kind: XpermsKind,
57 source_sid: SecurityId,
58 target_sid: SecurityId,
59 permission: KernelPermission,
60 xperms_prefix: u8,
61 ) -> KernelXpermsAccessDecision;
62}
63
64#[derive(Clone, PartialEq, Eq, zerocopy::IntoBytes, zerocopy::Immutable)]
65#[repr(C)]
66pub struct AccessQueryArgs {
67 pub source_sid: SecurityId,
68 pub target_sid: SecurityId,
69 pub target_class: KernelClass,
70}
71
72#[derive(Clone, Hash, PartialEq, Eq)]
73struct XpermsAccessQueryArgs {
74 xperms_kind: XpermsKind,
75 source_sid: SecurityId,
76 target_sid: SecurityId,
77 permission: KernelPermission,
78 xperms_prefix: u8,
79}
80
81struct FifoQueryCache {
83 access_cache: ConcurrentAccessCache,
84 create_sid_cache: ConcurrentSidCache,
85 xperms_access_cache: ConcurrentXpermsCache,
86}
87
88#[derive(Copy, Clone, Debug)]
89pub struct QueryCacheCapacity {
90 pub access_cache_capacity: usize,
93 pub sid_cache_capacity: usize,
94 pub xperms_cache_capacity: usize,
95}
96
97impl FifoQueryCache {
98 fn new(capacity: QueryCacheCapacity) -> Self {
100 Self {
101 access_cache: ConcurrentAccessCache::new(capacity.access_cache_capacity),
102 create_sid_cache: ConcurrentSidCache::new(capacity.sid_cache_capacity),
103 xperms_access_cache: ConcurrentXpermsCache::new(capacity.xperms_cache_capacity),
104 }
105 }
106
107 fn cache_stats(&self) -> CacheStats {
108 let stats = &self.access_cache.cache_stats() + &self.create_sid_cache.cache_stats();
109 &stats + &self.xperms_access_cache.cache_stats()
110 }
111
112 fn compute_kernel_access_decision(
113 &self,
114 delegate: &impl Query,
115 source_sid: SecurityId,
116 target_sid: SecurityId,
117 target_class: KernelClass,
118 ) -> KernelAccessDecision {
119 let query_args = AccessQueryArgs { source_sid, target_sid, target_class };
120 self.access_cache.get_or_insert(&query_args, || {
121 delegate.compute_access_decision(source_sid, target_sid, target_class)
122 })
123 }
124
125 fn compute_create_sid(
126 &self,
127 delegate: &impl Query,
128 source_sid: SecurityId,
129 target_sid: SecurityId,
130 target_class: KernelClass,
131 name: &[u8],
132 ) -> Result<SecurityId, anyhow::Error> {
133 if !name.is_empty() {
134 delegate.compute_create_sid(source_sid, target_sid, target_class, name)
135 } else {
136 let query_args = AccessQueryArgs { source_sid, target_sid, target_class };
137 self.create_sid_cache.get_or_try_insert(&query_args, || {
138 delegate.compute_create_sid(source_sid, target_sid, target_class, name)
139 })
140 }
141 }
142
143 fn compute_kernel_xperms_access_decision(
144 &self,
145 delegate: &impl Query,
146 xperms_kind: XpermsKind,
147 source_sid: SecurityId,
148 target_sid: SecurityId,
149 permission: KernelPermission,
150 xperms_prefix: u8,
151 ) -> KernelXpermsAccessDecision {
152 let query_args = XpermsAccessQueryArgs {
153 xperms_kind,
154 source_sid,
155 target_sid,
156 permission,
157 xperms_prefix,
158 };
159 self.xperms_access_cache.get_or_insert(&query_args, || {
160 delegate.compute_xperms_access_decision(
161 xperms_kind,
162 source_sid,
163 target_sid,
164 permission,
165 xperms_prefix,
166 )
167 })
168 }
169
170 fn reset(&self) {
171 self.access_cache.reset();
172 self.create_sid_cache.reset();
173 self.xperms_access_cache.reset();
174 }
175
176 #[cfg(test)]
178 fn access_cache_is_full(&self) -> bool {
179 self.access_cache.is_full()
180 }
181}
182
183pub const DEFAULT_SHARED_SIZE: QueryCacheCapacity = QueryCacheCapacity {
185 access_cache_capacity: 2048,
187 sid_cache_capacity: 2048,
189 xperms_cache_capacity: 512,
190};
191
192#[derive(Clone)]
194pub(crate) struct AccessVectorCache {
195 cache: Arc<FifoQueryCache>,
196 backend: Arc<SecurityServerBackend>,
197}
198
199impl AccessVectorCache {
200 pub fn new(backend: Arc<SecurityServerBackend>) -> Self {
201 let cache = FifoQueryCache::new(DEFAULT_SHARED_SIZE);
202 Self { cache: Arc::new(cache), backend }
203 }
204
205 pub fn cache_stats(&self) -> CacheStats {
206 self.cache.cache_stats()
207 }
208
209 pub fn reset(&self) {
210 self.cache.reset()
211 }
212}
213
214impl Query for AccessVectorCache {
215 fn compute_access_decision(
216 &self,
217 source_sid: SecurityId,
218 target_sid: SecurityId,
219 target_class: KernelClass,
220 ) -> KernelAccessDecision {
221 self.cache.compute_kernel_access_decision(
222 self.backend.as_ref(),
223 source_sid,
224 target_sid,
225 target_class,
226 )
227 }
228
229 fn compute_create_sid(
230 &self,
231 source_sid: SecurityId,
232 target_sid: SecurityId,
233 target_class: KernelClass,
234 name: &[u8],
235 ) -> Result<SecurityId, anyhow::Error> {
236 self.cache.compute_create_sid(
237 self.backend.as_ref(),
238 source_sid,
239 target_sid,
240 target_class,
241 name,
242 )
243 }
244
245 fn compute_xperms_access_decision(
246 &self,
247 xperms_kind: XpermsKind,
248 source_sid: SecurityId,
249 target_sid: SecurityId,
250 permission: KernelPermission,
251 xperms_prefix: u8,
252 ) -> KernelXpermsAccessDecision {
253 self.cache.compute_kernel_xperms_access_decision(
254 self.backend.as_ref(),
255 xperms_kind,
256 source_sid,
257 target_sid,
258 permission,
259 xperms_prefix,
260 )
261 }
262}
263
264#[cfg(test)]
266mod testing {
267 use super::*;
268 use crate::SecurityId;
269
270 use std::num::NonZeroU32;
271 use std::sync::LazyLock;
272 use std::sync::atomic::{AtomicU32, Ordering};
273
274 pub(super) static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
276
277 pub(super) const TEST_CAPACITY: QueryCacheCapacity = QueryCacheCapacity {
279 access_cache_capacity: 16,
280 sid_cache_capacity: 16,
281 xperms_cache_capacity: 4,
282 };
283
284 pub(super) fn unique_sid() -> SecurityId {
286 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
287 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::testing::*;
294 use super::*;
295 use crate::policy::{AccessVector, XpermsBitmap};
296 use crate::{KernelClass, ProcessPermission};
297
298 use std::sync::atomic::{AtomicUsize, Ordering};
299
300 #[derive(Default)]
302 struct TestDelegate {
303 query_count: AtomicUsize,
304 }
305
306 impl TestDelegate {
307 fn query_count(&self) -> usize {
308 self.query_count.load(Ordering::Relaxed)
309 }
310 }
311
312 impl Query for TestDelegate {
313 fn compute_access_decision(
314 &self,
315 _source_sid: SecurityId,
316 _target_sid: SecurityId,
317 _target_class: KernelClass,
318 ) -> KernelAccessDecision {
319 self.query_count.fetch_add(1, Ordering::Relaxed);
320 KernelAccessDecision {
321 allow: AccessVector::ALL,
322 audit: AccessVector::NONE,
323 flags: 0,
324 todo_bug: None,
325 }
326 }
327
328 fn compute_create_sid(
329 &self,
330 _source_sid: SecurityId,
331 _target_sid: SecurityId,
332 _target_class: KernelClass,
333 _name: &[u8],
334 ) -> Result<SecurityId, anyhow::Error> {
335 unreachable!()
336 }
337
338 fn compute_xperms_access_decision(
339 &self,
340 _xperms_kind: XpermsKind,
341 _source_sid: SecurityId,
342 _target_sid: SecurityId,
343 _permission: KernelPermission,
344 _xperms_prefix: u8,
345 ) -> KernelXpermsAccessDecision {
346 self.query_count.fetch_add(1, Ordering::Relaxed);
347 KernelXpermsAccessDecision {
348 allow: XpermsBitmap::ALL,
349 audit: XpermsBitmap::NONE,
350 permissive: false,
351 has_todo: false,
352 }
353 }
354 }
355
356 #[test]
357 fn fixed_access_vector_cache_add_entry() {
358 let delegate = TestDelegate::default();
359 let avc = FifoQueryCache::new(TEST_CAPACITY);
360 assert_eq!(0, delegate.query_count());
361 assert_eq!(
362 AccessVector::ALL,
363 avc.compute_kernel_access_decision(
364 &delegate,
365 A_TEST_SID.clone(),
366 A_TEST_SID.clone(),
367 KernelClass::Process
368 )
369 .allow
370 );
371 assert_eq!(1, delegate.query_count());
372 assert_eq!(
373 AccessVector::ALL,
374 avc.compute_kernel_access_decision(
375 &delegate,
376 A_TEST_SID.clone(),
377 A_TEST_SID.clone(),
378 KernelClass::Process
379 )
380 .allow
381 );
382 assert_eq!(1, delegate.query_count());
383 assert_eq!(false, avc.access_cache_is_full());
384 }
385
386 #[test]
387 fn fixed_access_vector_cache_reset() {
388 let delegate = TestDelegate::default();
389 let avc = FifoQueryCache::new(TEST_CAPACITY);
390
391 avc.reset();
392 assert_eq!(false, avc.access_cache_is_full());
393
394 assert_eq!(0, delegate.query_count());
395 assert_eq!(
396 AccessVector::ALL,
397 avc.compute_kernel_access_decision(
398 &delegate,
399 A_TEST_SID.clone(),
400 A_TEST_SID.clone(),
401 KernelClass::Process
402 )
403 .allow
404 );
405 assert_eq!(1, delegate.query_count());
406 assert_eq!(false, avc.access_cache_is_full());
407
408 avc.reset();
409 assert_eq!(false, avc.access_cache_is_full());
410 }
411
412 #[test]
413 fn access_vector_cache_ioctl_hit() {
414 let delegate = TestDelegate::default();
415 let avc = FifoQueryCache::new(TEST_CAPACITY);
416 assert_eq!(0, delegate.query_count());
417 assert_eq!(
418 XpermsBitmap::ALL,
419 avc.compute_kernel_xperms_access_decision(
420 &delegate,
421 XpermsKind::Ioctl,
422 A_TEST_SID.clone(),
423 A_TEST_SID.clone(),
424 ProcessPermission::Fork.into(),
425 0x0,
426 )
427 .allow
428 );
429 assert_eq!(1, delegate.query_count());
430 assert_eq!(
432 XpermsBitmap::ALL,
433 avc.compute_kernel_xperms_access_decision(
434 &delegate,
435 XpermsKind::Ioctl,
436 A_TEST_SID.clone(),
437 A_TEST_SID.clone(),
438 ProcessPermission::Fork.into(),
439 0x0
440 )
441 .allow
442 );
443 assert_eq!(1, delegate.query_count());
444 }
445
446 #[test]
447 fn access_vector_cache_nlmsg_hit() {
448 let delegate = TestDelegate::default();
449 let avc = FifoQueryCache::new(TEST_CAPACITY);
450 assert_eq!(0, delegate.query_count());
451 assert_eq!(
452 XpermsBitmap::ALL,
453 avc.compute_kernel_xperms_access_decision(
454 &delegate,
455 XpermsKind::Nlmsg,
456 A_TEST_SID.clone(),
457 A_TEST_SID.clone(),
458 ProcessPermission::Fork.into(),
459 0x0,
460 )
461 .allow
462 );
463 assert_eq!(1, delegate.query_count());
464 assert_eq!(
466 XpermsBitmap::ALL,
467 avc.compute_kernel_xperms_access_decision(
468 &delegate,
469 XpermsKind::Nlmsg,
470 A_TEST_SID.clone(),
471 A_TEST_SID.clone(),
472 ProcessPermission::Fork.into(),
473 0x0
474 )
475 .allow
476 );
477 assert_eq!(1, delegate.query_count());
478 }
479
480 #[test]
481 fn access_vector_cache_nlmsg_and_ioctl() {
482 let delegate = TestDelegate::default();
483 let avc = FifoQueryCache::new(TEST_CAPACITY);
484
485 avc.compute_kernel_xperms_access_decision(
486 &delegate,
487 XpermsKind::Ioctl,
488 A_TEST_SID.clone(),
489 A_TEST_SID.clone(),
490 ProcessPermission::Fork.into(),
491 0x0,
492 );
493 assert_eq!(avc.cache_stats().allocs, 1);
494
495 avc.compute_kernel_xperms_access_decision(
498 &delegate,
499 XpermsKind::Nlmsg,
500 A_TEST_SID.clone(),
501 A_TEST_SID.clone(),
502 ProcessPermission::Fork.into(),
503 0x0,
504 );
505 assert_eq!(avc.cache_stats().allocs, 2);
506 }
507}