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_create_sid(source_sid, target_sid, fs_node_class.into())
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.kernel_permissions_to_access_vector(&[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 = ObjectClass::from(permission.class());
208
209 let permission_decision = query.compute_access_decision(source_sid, target_sid, target_class);
210
211 let [xperms_postfix, xperms_prefix] = xperm.to_le_bytes();
212 let xperms_decision = query.compute_xperms_access_decision(
213 xperms_kind,
214 source_sid,
215 target_sid,
216 target_class,
217 xperms_prefix,
218 );
219
220 let mut result = if let Some(permission_access_vector) =
221 access_vector_computer.kernel_permissions_to_access_vector(&[permission])
222 {
223 let permit = (permission_access_vector & permission_decision.allow
224 == permission_access_vector)
225 && xperms_decision.allow.contains(xperms_postfix);
226 let audit = if permit {
227 (permission_access_vector & permission_decision.auditallow == permission_access_vector)
228 && xperms_decision.auditallow.contains(xperms_postfix)
229 } else {
230 (permission_access_vector & permission_decision.auditdeny == permission_access_vector)
231 && xperms_decision.auditdeny.contains(xperms_postfix)
232 };
233 PermissionCheckResult { permit, audit, todo_bug: None }
234 } else {
235 PermissionCheckResult { permit: false, audit: true, todo_bug: None }
236 };
237
238 if !result.permit {
239 if !is_enforcing {
240 result.permit = true;
241 } else if permission_decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
242 result.permit = true;
243 } else if permission_decision.todo_bug.is_some() {
244 result.permit = true;
247 result.todo_bug = permission_decision.todo_bug;
248 }
249 }
250
251 result
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::policy::testing::{ACCESS_VECTOR_0001, ACCESS_VECTOR_0010};
258 use crate::policy::{AccessDecision, AccessVector, XpermsAccessDecision};
259 use crate::{
260 CommonFsNodePermission, FileClass, FilePermission, ForClass, ObjectClass, ProcessPermission,
261 };
262
263 use std::num::NonZeroU32;
264 use std::sync::LazyLock;
265 use std::sync::atomic::{AtomicU32, Ordering};
266
267 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
269
270 fn unique_sid() -> SecurityId {
272 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
273 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
274 }
275
276 fn access_vector_from_permission<P: ClassPermission + Into<KernelPermission> + 'static>(
277 permission: P,
278 ) -> AccessVector {
279 match permission.into() {
280 KernelPermission::Process(ProcessPermission::Fork) => ACCESS_VECTOR_0001,
282 KernelPermission::Process(ProcessPermission::Transition) => ACCESS_VECTOR_0010,
283 KernelPermission::File(FilePermission::Ioctl) => ACCESS_VECTOR_0001,
285 _ => AccessVector::NONE,
286 }
287 }
288
289 fn access_vector_from_permissions<
290 'a,
291 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
292 >(
293 permissions: &[P],
294 ) -> AccessVector {
295 let mut access_vector = AccessVector::NONE;
296 for permission in permissions {
297 access_vector |= access_vector_from_permission(permission.clone());
298 }
299 access_vector
300 }
301
302 #[derive(Default)]
303 pub struct DenyAllPermissions;
304
305 impl Query for DenyAllPermissions {
306 fn compute_access_decision(
307 &self,
308 _source_sid: SecurityId,
309 _target_sid: SecurityId,
310 _target_class: ObjectClass,
311 ) -> AccessDecision {
312 AccessDecision::allow(AccessVector::NONE)
313 }
314
315 fn compute_create_sid(
316 &self,
317 _source_sid: SecurityId,
318 _target_sid: SecurityId,
319 _target_class: ObjectClass,
320 ) -> Result<SecurityId, anyhow::Error> {
321 unreachable!();
322 }
323
324 fn compute_new_fs_node_sid_with_name(
325 &self,
326 _source_sid: SecurityId,
327 _target_sid: SecurityId,
328 _fs_node_class: FsNodeClass,
329 _fs_node_name: NullessByteStr<'_>,
330 ) -> Option<SecurityId> {
331 unreachable!();
332 }
333
334 fn compute_xperms_access_decision(
335 &self,
336 _xperms_kind: XpermsKind,
337 _source_sid: SecurityId,
338 _target_sid: SecurityId,
339 _target_class: ObjectClass,
340 _xperms_prefix: u8,
341 ) -> XpermsAccessDecision {
342 XpermsAccessDecision::DENY_ALL
343 }
344 }
345
346 impl AccessVectorComputer for DenyAllPermissions {
347 fn kernel_permissions_to_access_vector<
348 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
349 >(
350 &self,
351 permissions: &[P],
352 ) -> Option<AccessVector> {
353 Some(access_vector_from_permissions(permissions))
354 }
355 }
356
357 #[derive(Default)]
359 struct AllowAllPermissions;
360
361 impl Query for AllowAllPermissions {
362 fn compute_access_decision(
363 &self,
364 _source_sid: SecurityId,
365 _target_sid: SecurityId,
366 _target_class: ObjectClass,
367 ) -> AccessDecision {
368 AccessDecision::allow(AccessVector::ALL)
369 }
370
371 fn compute_create_sid(
372 &self,
373 _source_sid: SecurityId,
374 _target_sid: SecurityId,
375 _target_class: ObjectClass,
376 ) -> Result<SecurityId, anyhow::Error> {
377 unreachable!();
378 }
379
380 fn compute_new_fs_node_sid_with_name(
381 &self,
382 _source_sid: SecurityId,
383 _target_sid: SecurityId,
384 _fs_node_class: FsNodeClass,
385 _fs_node_name: NullessByteStr<'_>,
386 ) -> Option<SecurityId> {
387 unreachable!();
388 }
389
390 fn compute_xperms_access_decision(
391 &self,
392 _xperms_kind: XpermsKind,
393 _source_sid: SecurityId,
394 _target_sid: SecurityId,
395 _target_class: ObjectClass,
396 _xperms_prefix: u8,
397 ) -> XpermsAccessDecision {
398 XpermsAccessDecision::ALLOW_ALL
399 }
400 }
401
402 impl AccessVectorComputer for AllowAllPermissions {
403 fn kernel_permissions_to_access_vector<
404 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
405 >(
406 &self,
407 permissions: &[P],
408 ) -> Option<AccessVector> {
409 Some(access_vector_from_permissions(permissions))
410 }
411 }
412
413 #[derive(Default)]
415 struct DenyPermissionsAllowXperms;
416
417 impl Query for DenyPermissionsAllowXperms {
418 fn compute_access_decision(
419 &self,
420 _source_sid: SecurityId,
421 _target_sid: SecurityId,
422 _target_class: ObjectClass,
423 ) -> AccessDecision {
424 AccessDecision::allow(AccessVector::NONE)
425 }
426
427 fn compute_create_sid(
428 &self,
429 _source_sid: SecurityId,
430 _target_sid: SecurityId,
431 _target_class: ObjectClass,
432 ) -> Result<SecurityId, anyhow::Error> {
433 unreachable!();
434 }
435
436 fn compute_new_fs_node_sid_with_name(
437 &self,
438 _source_sid: SecurityId,
439 _target_sid: SecurityId,
440 _fs_node_class: FsNodeClass,
441 _fs_node_name: NullessByteStr<'_>,
442 ) -> Option<SecurityId> {
443 unreachable!();
444 }
445
446 fn compute_xperms_access_decision(
447 &self,
448 _xperms_kind: XpermsKind,
449 _source_sid: SecurityId,
450 _target_sid: SecurityId,
451 _target_class: ObjectClass,
452 _xperms_prefix: u8,
453 ) -> XpermsAccessDecision {
454 XpermsAccessDecision::ALLOW_ALL
455 }
456 }
457
458 impl AccessVectorComputer for DenyPermissionsAllowXperms {
459 fn kernel_permissions_to_access_vector<
460 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
461 >(
462 &self,
463 permissions: &[P],
464 ) -> Option<AccessVector> {
465 Some(access_vector_from_permissions(permissions))
466 }
467 }
468
469 #[derive(Default)]
471 struct AllowPermissionsDenyXperms;
472
473 impl Query for AllowPermissionsDenyXperms {
474 fn compute_access_decision(
475 &self,
476 _source_sid: SecurityId,
477 _target_sid: SecurityId,
478 _target_class: ObjectClass,
479 ) -> AccessDecision {
480 AccessDecision::allow(AccessVector::ALL)
481 }
482
483 fn compute_create_sid(
484 &self,
485 _source_sid: SecurityId,
486 _target_sid: SecurityId,
487 _target_class: ObjectClass,
488 ) -> Result<SecurityId, anyhow::Error> {
489 unreachable!();
490 }
491
492 fn compute_new_fs_node_sid_with_name(
493 &self,
494 _source_sid: SecurityId,
495 _target_sid: SecurityId,
496 _fs_node_class: FsNodeClass,
497 _fs_node_name: NullessByteStr<'_>,
498 ) -> Option<SecurityId> {
499 unreachable!();
500 }
501
502 fn compute_xperms_access_decision(
503 &self,
504 _xperms_kind: XpermsKind,
505 _source_sid: SecurityId,
506 _target_sid: SecurityId,
507 _target_class: ObjectClass,
508 _xperms_prefix: u8,
509 ) -> XpermsAccessDecision {
510 XpermsAccessDecision::DENY_ALL
511 }
512 }
513
514 impl AccessVectorComputer for AllowPermissionsDenyXperms {
515 fn kernel_permissions_to_access_vector<
516 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
517 >(
518 &self,
519 permissions: &[P],
520 ) -> Option<AccessVector> {
521 Some(access_vector_from_permissions(permissions))
522 }
523 }
524
525 #[test]
526 fn has_permission_both() {
527 let deny_all = DenyAllPermissions::default();
528 let allow_all = AllowAllPermissions::default();
529
530 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
533 for permission in &permissions {
534 assert_eq!(
536 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
537 has_permission(
538 true,
539 &deny_all,
540 &deny_all,
541 *A_TEST_SID,
542 *A_TEST_SID,
543 permission.clone()
544 )
545 );
546 assert_eq!(
548 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
549 has_permission(
550 true,
551 &allow_all,
552 &allow_all,
553 *A_TEST_SID,
554 *A_TEST_SID,
555 permission.clone()
556 )
557 );
558 }
559 }
560
561 #[test]
562 fn has_ioctl_permission_enforcing() {
563 let deny_all = DenyAllPermissions::default();
564 let allow_all = AllowAllPermissions::default();
565 let deny_perms_allow_xperms = DenyPermissionsAllowXperms::default();
566 let allow_perms_deny_xperms = AllowPermissionsDenyXperms::default();
567 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
568
569 assert_eq!(
571 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
572 has_extended_permission(
573 true,
574 &deny_all,
575 &deny_all,
576 XpermsKind::Ioctl,
577 *A_TEST_SID,
578 *A_TEST_SID,
579 permission.clone(),
580 0xabcd
581 )
582 );
583 assert_eq!(
585 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
586 has_extended_permission(
587 true,
588 &allow_all,
589 &allow_all,
590 XpermsKind::Ioctl,
591 *A_TEST_SID,
592 *A_TEST_SID,
593 permission.clone(),
594 0xabcd
595 )
596 );
597 assert_eq!(
599 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
600 has_extended_permission(
601 true,
602 &deny_perms_allow_xperms,
603 &deny_perms_allow_xperms,
604 XpermsKind::Ioctl,
605 *A_TEST_SID,
606 *A_TEST_SID,
607 permission.clone(),
608 0xabcd
609 )
610 );
611 assert_eq!(
613 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
614 has_extended_permission(
615 true,
616 &allow_perms_deny_xperms,
617 &allow_perms_deny_xperms,
618 XpermsKind::Ioctl,
619 *A_TEST_SID,
620 *A_TEST_SID,
621 permission,
622 0xabcd
623 )
624 );
625 }
626
627 #[test]
628 fn has_ioctl_permission_not_enforcing() {
629 let deny_all = DenyAllPermissions::default();
630 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
631
632 assert_eq!(
635 PermissionCheckResult { permit: true, audit: true, todo_bug: None },
636 has_extended_permission(
637 false,
638 &deny_all,
639 &deny_all,
640 XpermsKind::Ioctl,
641 *A_TEST_SID,
642 *A_TEST_SID,
643 permission,
644 0xabcd
645 )
646 );
647 }
648}