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