1use crate::access_vector_cache::{AccessVectorCache, Query};
6use crate::policy::{
7 AccessDecision, AccessVector, AccessVectorComputer, SELINUX_AVD_FLAGS_PERMISSIVE, XpermsKind,
8};
9use crate::security_server::SecurityServer;
10use crate::{
11 ClassPermission, FsNodeClass, KernelPermission, NullessByteStr, ObjectClass, SecurityId,
12};
13
14use std::num::NonZeroU64;
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct PermissionCheckResult {
19 pub permit: bool,
21
22 pub audit: bool,
27
28 pub todo_bug: Option<NonZeroU64>,
31}
32
33pub struct PermissionCheck<'a> {
37 security_server: &'a SecurityServer,
38 access_vector_cache: &'a AccessVectorCache,
39}
40
41impl<'a> PermissionCheck<'a> {
42 pub(crate) fn new(
43 security_server: &'a SecurityServer,
44 access_vector_cache: &'a AccessVectorCache,
45 ) -> Self {
46 Self { security_server, access_vector_cache }
47 }
48
49 pub fn has_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
53 &self,
54 source_sid: SecurityId,
55 target_sid: SecurityId,
56 permission: P,
57 ) -> PermissionCheckResult {
58 has_permission(
59 self.security_server.is_enforcing(),
60 self.access_vector_cache,
61 self.security_server,
62 source_sid,
63 target_sid,
64 permission,
65 )
66 }
67
68 pub fn has_extended_permission<
82 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
83 >(
84 &self,
85 xperms_kind: XpermsKind,
86 source_sid: SecurityId,
87 target_sid: SecurityId,
88 permission: P,
89 xperm: u16,
90 ) -> PermissionCheckResult {
91 has_extended_permission(
92 self.security_server.is_enforcing(),
93 self.access_vector_cache,
94 self.security_server,
95 xperms_kind,
96 source_sid,
97 target_sid,
98 permission,
99 xperm,
100 )
101 }
102
103 pub fn security_server(&self) -> &SecurityServer {
106 self.security_server
107 }
108
109 pub fn compute_new_fs_node_sid(
114 &self,
115 source_sid: SecurityId,
116 target_sid: SecurityId,
117 fs_node_class: FsNodeClass,
118 fs_node_name: NullessByteStr<'_>,
119 ) -> Result<SecurityId, anyhow::Error> {
120 if !fs_node_name.as_bytes().is_empty() {
122 if let Some(sid) = self.access_vector_cache.compute_new_fs_node_sid_with_name(
123 source_sid,
124 target_sid,
125 fs_node_class,
126 fs_node_name,
127 ) {
128 return Ok(sid);
129 }
130 }
131 self.access_vector_cache.compute_new_fs_node_sid(source_sid, target_sid, fs_node_class)
132 }
133
134 pub fn compute_access_decision(
136 &self,
137 source_sid: SecurityId,
138 target_sid: SecurityId,
139 target_class: ObjectClass,
140 ) -> AccessDecision {
141 self.access_vector_cache.compute_access_decision(source_sid, target_sid, target_class)
142 }
143}
144
145fn has_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
147 is_enforcing: bool,
148 query: &impl Query,
149 access_vector_computer: &impl AccessVectorComputer,
150 source_sid: SecurityId,
151 target_sid: SecurityId,
152 permission: P,
153) -> PermissionCheckResult {
154 let target_class = permission.class();
155
156 let decision = query.compute_access_decision(source_sid, target_sid, target_class.into());
157
158 let mut result = if let Some(permission_access_vector) =
159 access_vector_computer.access_vector_from_permissions(&[permission])
160 {
161 let permit = permission_access_vector & decision.allow == permission_access_vector;
162 let audit = if permit {
163 permission_access_vector & decision.auditallow != AccessVector::NONE
164 } else {
165 permission_access_vector & decision.auditdeny != AccessVector::NONE
166 };
167 PermissionCheckResult { permit, audit, todo_bug: None }
168 } else {
169 PermissionCheckResult { permit: false, audit: true, todo_bug: None }
170 };
171
172 if !result.permit {
173 if decision.todo_bug.is_some() {
174 result.permit = true;
181 result.todo_bug = decision.todo_bug;
182 } else if !is_enforcing {
183 result.permit = true;
185 } else if decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
186 result.permit = true;
189 }
190 }
191
192 result
193}
194
195fn has_extended_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
198 is_enforcing: bool,
199 query: &impl Query,
200 access_vector_computer: &impl AccessVectorComputer,
201 xperms_kind: XpermsKind,
202 source_sid: SecurityId,
203 target_sid: SecurityId,
204 permission: P,
205 xperm: u16,
206) -> PermissionCheckResult {
207 let target_class = permission.class();
208
209 let permission_decision =
210 query.compute_access_decision(source_sid, target_sid, target_class.into());
211
212 let [xperms_postfix, xperms_prefix] = xperm.to_le_bytes();
213 let xperms_decision = query.compute_xperms_access_decision(
214 xperms_kind,
215 source_sid,
216 target_sid,
217 target_class.into(),
218 xperms_prefix,
219 );
220
221 let mut result = if let Some(permission_access_vector) =
222 access_vector_computer.access_vector_from_permissions(&[permission])
223 {
224 let permit = (permission_access_vector & permission_decision.allow
225 == permission_access_vector)
226 && xperms_decision.allow.contains(xperms_postfix);
227 let audit = if permit {
228 (permission_access_vector & permission_decision.auditallow == permission_access_vector)
229 && xperms_decision.auditallow.contains(xperms_postfix)
230 } else {
231 (permission_access_vector & permission_decision.auditdeny == permission_access_vector)
232 && xperms_decision.auditdeny.contains(xperms_postfix)
233 };
234 PermissionCheckResult { permit, audit, todo_bug: None }
235 } else {
236 PermissionCheckResult { permit: false, audit: true, todo_bug: None }
237 };
238
239 if !result.permit {
240 if !is_enforcing {
241 result.permit = true;
242 } else if permission_decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
243 result.permit = true;
244 } else if permission_decision.todo_bug.is_some() {
245 result.permit = true;
248 result.todo_bug = permission_decision.todo_bug;
249 }
250 }
251
252 result
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::policy::testing::{ACCESS_VECTOR_0001, ACCESS_VECTOR_0010};
259 use crate::policy::{AccessDecision, AccessVector, XpermsAccessDecision};
260 use crate::{
261 CommonFilePermission, CommonFsNodePermission, FileClass, FilePermission, ForClass,
262 ObjectClass, ProcessPermission,
263 };
264
265 use std::num::NonZeroU32;
266 use std::sync::LazyLock;
267 use std::sync::atomic::{AtomicU32, Ordering};
268
269 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
271
272 fn unique_sid() -> SecurityId {
274 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
275 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
276 }
277
278 fn access_vector_from_permission<P: ClassPermission + Into<KernelPermission> + 'static>(
279 permission: P,
280 ) -> AccessVector {
281 match permission.into() {
282 KernelPermission::Process(ProcessPermission::Fork) => ACCESS_VECTOR_0001,
284 KernelPermission::Process(ProcessPermission::Transition) => ACCESS_VECTOR_0010,
285 KernelPermission::File(FilePermission::Common(CommonFilePermission::Common(
287 CommonFsNodePermission::Ioctl,
288 ))) => ACCESS_VECTOR_0001,
289 _ => AccessVector::NONE,
290 }
291 }
292
293 fn access_vector_from_permissions<
294 'a,
295 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
296 >(
297 permissions: &[P],
298 ) -> AccessVector {
299 let mut access_vector = AccessVector::NONE;
300 for permission in permissions {
301 access_vector |= access_vector_from_permission(permission.clone());
302 }
303 access_vector
304 }
305
306 #[derive(Default)]
307 pub struct DenyAllPermissions;
308
309 impl Query for DenyAllPermissions {
310 fn compute_access_decision(
311 &self,
312 _source_sid: SecurityId,
313 _target_sid: SecurityId,
314 _target_class: ObjectClass,
315 ) -> AccessDecision {
316 AccessDecision::allow(AccessVector::NONE)
317 }
318
319 fn compute_new_fs_node_sid(
320 &self,
321 _source_sid: SecurityId,
322 _target_sid: SecurityId,
323 _fs_node_class: FsNodeClass,
324 ) -> Result<SecurityId, anyhow::Error> {
325 unreachable!();
326 }
327
328 fn compute_new_fs_node_sid_with_name(
329 &self,
330 _source_sid: SecurityId,
331 _target_sid: SecurityId,
332 _fs_node_class: FsNodeClass,
333 _fs_node_name: NullessByteStr<'_>,
334 ) -> Option<SecurityId> {
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 _target_class: ObjectClass,
344 _xperms_prefix: u8,
345 ) -> XpermsAccessDecision {
346 XpermsAccessDecision::DENY_ALL
347 }
348 }
349
350 impl AccessVectorComputer for DenyAllPermissions {
351 fn access_vector_from_permissions<
352 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
353 >(
354 &self,
355 permissions: &[P],
356 ) -> Option<AccessVector> {
357 Some(access_vector_from_permissions(permissions))
358 }
359 }
360
361 #[derive(Default)]
363 struct AllowAllPermissions;
364
365 impl Query for AllowAllPermissions {
366 fn compute_access_decision(
367 &self,
368 _source_sid: SecurityId,
369 _target_sid: SecurityId,
370 _target_class: ObjectClass,
371 ) -> AccessDecision {
372 AccessDecision::allow(AccessVector::ALL)
373 }
374
375 fn compute_new_fs_node_sid(
376 &self,
377 _source_sid: SecurityId,
378 _target_sid: SecurityId,
379 _fs_node_class: FsNodeClass,
380 ) -> Result<SecurityId, anyhow::Error> {
381 unreachable!();
382 }
383
384 fn compute_new_fs_node_sid_with_name(
385 &self,
386 _source_sid: SecurityId,
387 _target_sid: SecurityId,
388 _fs_node_class: FsNodeClass,
389 _fs_node_name: NullessByteStr<'_>,
390 ) -> Option<SecurityId> {
391 unreachable!();
392 }
393
394 fn compute_xperms_access_decision(
395 &self,
396 _xperms_kind: XpermsKind,
397 _source_sid: SecurityId,
398 _target_sid: SecurityId,
399 _target_class: ObjectClass,
400 _xperms_prefix: u8,
401 ) -> XpermsAccessDecision {
402 XpermsAccessDecision::ALLOW_ALL
403 }
404 }
405
406 impl AccessVectorComputer for AllowAllPermissions {
407 fn access_vector_from_permissions<
408 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
409 >(
410 &self,
411 permissions: &[P],
412 ) -> Option<AccessVector> {
413 Some(access_vector_from_permissions(permissions))
414 }
415 }
416
417 #[derive(Default)]
419 struct DenyPermissionsAllowXperms;
420
421 impl Query for DenyPermissionsAllowXperms {
422 fn compute_access_decision(
423 &self,
424 _source_sid: SecurityId,
425 _target_sid: SecurityId,
426 _target_class: ObjectClass,
427 ) -> AccessDecision {
428 AccessDecision::allow(AccessVector::NONE)
429 }
430
431 fn compute_new_fs_node_sid(
432 &self,
433 _source_sid: SecurityId,
434 _target_sid: SecurityId,
435 _fs_node_class: FsNodeClass,
436 ) -> Result<SecurityId, anyhow::Error> {
437 unreachable!();
438 }
439
440 fn compute_new_fs_node_sid_with_name(
441 &self,
442 _source_sid: SecurityId,
443 _target_sid: SecurityId,
444 _fs_node_class: FsNodeClass,
445 _fs_node_name: NullessByteStr<'_>,
446 ) -> Option<SecurityId> {
447 unreachable!();
448 }
449
450 fn compute_xperms_access_decision(
451 &self,
452 _xperms_kind: XpermsKind,
453 _source_sid: SecurityId,
454 _target_sid: SecurityId,
455 _target_class: ObjectClass,
456 _xperms_prefix: u8,
457 ) -> XpermsAccessDecision {
458 XpermsAccessDecision::ALLOW_ALL
459 }
460 }
461
462 impl AccessVectorComputer for DenyPermissionsAllowXperms {
463 fn access_vector_from_permissions<
464 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
465 >(
466 &self,
467 permissions: &[P],
468 ) -> Option<AccessVector> {
469 Some(access_vector_from_permissions(permissions))
470 }
471 }
472
473 #[derive(Default)]
475 struct AllowPermissionsDenyXperms;
476
477 impl Query for AllowPermissionsDenyXperms {
478 fn compute_access_decision(
479 &self,
480 _source_sid: SecurityId,
481 _target_sid: SecurityId,
482 _target_class: ObjectClass,
483 ) -> AccessDecision {
484 AccessDecision::allow(AccessVector::ALL)
485 }
486
487 fn compute_new_fs_node_sid(
488 &self,
489 _source_sid: SecurityId,
490 _target_sid: SecurityId,
491 _fs_node_class: FsNodeClass,
492 ) -> Result<SecurityId, anyhow::Error> {
493 unreachable!();
494 }
495
496 fn compute_new_fs_node_sid_with_name(
497 &self,
498 _source_sid: SecurityId,
499 _target_sid: SecurityId,
500 _fs_node_class: FsNodeClass,
501 _fs_node_name: NullessByteStr<'_>,
502 ) -> Option<SecurityId> {
503 unreachable!();
504 }
505
506 fn compute_xperms_access_decision(
507 &self,
508 _xperms_kind: XpermsKind,
509 _source_sid: SecurityId,
510 _target_sid: SecurityId,
511 _target_class: ObjectClass,
512 _xperms_prefix: u8,
513 ) -> XpermsAccessDecision {
514 XpermsAccessDecision::DENY_ALL
515 }
516 }
517
518 impl AccessVectorComputer for AllowPermissionsDenyXperms {
519 fn access_vector_from_permissions<
520 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
521 >(
522 &self,
523 permissions: &[P],
524 ) -> Option<AccessVector> {
525 Some(access_vector_from_permissions(permissions))
526 }
527 }
528
529 #[test]
530 fn has_permission_both() {
531 let deny_all: DenyAllPermissions = Default::default();
532 let allow_all: AllowAllPermissions = Default::default();
533
534 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
537 for permission in &permissions {
538 assert_eq!(
540 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
541 has_permission(
542 true,
543 &deny_all,
544 &deny_all,
545 *A_TEST_SID,
546 *A_TEST_SID,
547 permission.clone()
548 )
549 );
550 assert_eq!(
552 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
553 has_permission(
554 true,
555 &allow_all,
556 &allow_all,
557 *A_TEST_SID,
558 *A_TEST_SID,
559 permission.clone()
560 )
561 );
562 }
563 }
564
565 #[test]
566 fn has_ioctl_permission_enforcing() {
567 let deny_all: DenyAllPermissions = Default::default();
568 let allow_all: AllowAllPermissions = Default::default();
569 let deny_perms_allow_xperms: DenyPermissionsAllowXperms = Default::default();
570 let allow_perms_deny_xperms: AllowPermissionsDenyXperms = Default::default();
571 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
572
573 assert_eq!(
575 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
576 has_extended_permission(
577 true,
578 &deny_all,
579 &deny_all,
580 XpermsKind::Ioctl,
581 *A_TEST_SID,
582 *A_TEST_SID,
583 permission.clone(),
584 0xabcd
585 )
586 );
587 assert_eq!(
589 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
590 has_extended_permission(
591 true,
592 &allow_all,
593 &allow_all,
594 XpermsKind::Ioctl,
595 *A_TEST_SID,
596 *A_TEST_SID,
597 permission.clone(),
598 0xabcd
599 )
600 );
601 assert_eq!(
603 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
604 has_extended_permission(
605 true,
606 &deny_perms_allow_xperms,
607 &deny_perms_allow_xperms,
608 XpermsKind::Ioctl,
609 *A_TEST_SID,
610 *A_TEST_SID,
611 permission.clone(),
612 0xabcd
613 )
614 );
615 assert_eq!(
617 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
618 has_extended_permission(
619 true,
620 &allow_perms_deny_xperms,
621 &allow_perms_deny_xperms,
622 XpermsKind::Ioctl,
623 *A_TEST_SID,
624 *A_TEST_SID,
625 permission,
626 0xabcd
627 )
628 );
629 }
630
631 #[test]
632 fn has_ioctl_permission_not_enforcing() {
633 let deny_all: DenyAllPermissions = Default::default();
634 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
635
636 assert_eq!(
639 PermissionCheckResult { permit: true, audit: true, todo_bug: None },
640 has_extended_permission(
641 false,
642 &deny_all,
643 &deny_all,
644 XpermsKind::Ioctl,
645 *A_TEST_SID,
646 *A_TEST_SID,
647 permission,
648 0xabcd
649 )
650 );
651 }
652}