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