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 = 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.access_vector_from_permissions(&[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 CommonFilePermission, CommonFsNodePermission, FileClass, FilePermission, ForClass,
261 ObjectClass, ProcessPermission,
262 };
263
264 use std::num::NonZeroU32;
265 use std::sync::LazyLock;
266 use std::sync::atomic::{AtomicU32, Ordering};
267
268 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
270
271 fn unique_sid() -> SecurityId {
273 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
274 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
275 }
276
277 fn access_vector_from_permission<P: ClassPermission + Into<KernelPermission> + 'static>(
278 permission: P,
279 ) -> AccessVector {
280 match permission.into() {
281 KernelPermission::Process(ProcessPermission::Fork) => ACCESS_VECTOR_0001,
283 KernelPermission::Process(ProcessPermission::Transition) => ACCESS_VECTOR_0010,
284 KernelPermission::File(FilePermission::Common(CommonFilePermission::Common(
286 CommonFsNodePermission::Ioctl,
287 ))) => ACCESS_VECTOR_0001,
288 _ => AccessVector::NONE,
289 }
290 }
291
292 fn access_vector_from_permissions<
293 'a,
294 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
295 >(
296 permissions: &[P],
297 ) -> AccessVector {
298 let mut access_vector = AccessVector::NONE;
299 for permission in permissions {
300 access_vector |= access_vector_from_permission(permission.clone());
301 }
302 access_vector
303 }
304
305 #[derive(Default)]
306 pub struct DenyAllPermissions;
307
308 impl Query for DenyAllPermissions {
309 fn compute_access_decision(
310 &self,
311 _source_sid: SecurityId,
312 _target_sid: SecurityId,
313 _target_class: ObjectClass,
314 ) -> AccessDecision {
315 AccessDecision::allow(AccessVector::NONE)
316 }
317
318 fn compute_new_fs_node_sid(
319 &self,
320 _source_sid: SecurityId,
321 _target_sid: SecurityId,
322 _fs_node_class: FsNodeClass,
323 ) -> Result<SecurityId, anyhow::Error> {
324 unreachable!();
325 }
326
327 fn compute_new_fs_node_sid_with_name(
328 &self,
329 _source_sid: SecurityId,
330 _target_sid: SecurityId,
331 _fs_node_class: FsNodeClass,
332 _fs_node_name: NullessByteStr<'_>,
333 ) -> Option<SecurityId> {
334 unreachable!();
335 }
336
337 fn compute_xperms_access_decision(
338 &self,
339 _xperms_kind: XpermsKind,
340 _source_sid: SecurityId,
341 _target_sid: SecurityId,
342 _target_class: ObjectClass,
343 _xperms_prefix: u8,
344 ) -> XpermsAccessDecision {
345 XpermsAccessDecision::DENY_ALL
346 }
347 }
348
349 impl AccessVectorComputer for DenyAllPermissions {
350 fn access_vector_from_permissions<
351 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
352 >(
353 &self,
354 permissions: &[P],
355 ) -> Option<AccessVector> {
356 Some(access_vector_from_permissions(permissions))
357 }
358 }
359
360 #[derive(Default)]
362 struct AllowAllPermissions;
363
364 impl Query for AllowAllPermissions {
365 fn compute_access_decision(
366 &self,
367 _source_sid: SecurityId,
368 _target_sid: SecurityId,
369 _target_class: ObjectClass,
370 ) -> AccessDecision {
371 AccessDecision::allow(AccessVector::ALL)
372 }
373
374 fn compute_new_fs_node_sid(
375 &self,
376 _source_sid: SecurityId,
377 _target_sid: SecurityId,
378 _fs_node_class: FsNodeClass,
379 ) -> Result<SecurityId, anyhow::Error> {
380 unreachable!();
381 }
382
383 fn compute_new_fs_node_sid_with_name(
384 &self,
385 _source_sid: SecurityId,
386 _target_sid: SecurityId,
387 _fs_node_class: FsNodeClass,
388 _fs_node_name: NullessByteStr<'_>,
389 ) -> Option<SecurityId> {
390 unreachable!();
391 }
392
393 fn compute_xperms_access_decision(
394 &self,
395 _xperms_kind: XpermsKind,
396 _source_sid: SecurityId,
397 _target_sid: SecurityId,
398 _target_class: ObjectClass,
399 _xperms_prefix: u8,
400 ) -> XpermsAccessDecision {
401 XpermsAccessDecision::ALLOW_ALL
402 }
403 }
404
405 impl AccessVectorComputer for AllowAllPermissions {
406 fn access_vector_from_permissions<
407 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
408 >(
409 &self,
410 permissions: &[P],
411 ) -> Option<AccessVector> {
412 Some(access_vector_from_permissions(permissions))
413 }
414 }
415
416 #[derive(Default)]
418 struct DenyPermissionsAllowXperms;
419
420 impl Query for DenyPermissionsAllowXperms {
421 fn compute_access_decision(
422 &self,
423 _source_sid: SecurityId,
424 _target_sid: SecurityId,
425 _target_class: ObjectClass,
426 ) -> AccessDecision {
427 AccessDecision::allow(AccessVector::NONE)
428 }
429
430 fn compute_new_fs_node_sid(
431 &self,
432 _source_sid: SecurityId,
433 _target_sid: SecurityId,
434 _fs_node_class: FsNodeClass,
435 ) -> Result<SecurityId, anyhow::Error> {
436 unreachable!();
437 }
438
439 fn compute_new_fs_node_sid_with_name(
440 &self,
441 _source_sid: SecurityId,
442 _target_sid: SecurityId,
443 _fs_node_class: FsNodeClass,
444 _fs_node_name: NullessByteStr<'_>,
445 ) -> Option<SecurityId> {
446 unreachable!();
447 }
448
449 fn compute_xperms_access_decision(
450 &self,
451 _xperms_kind: XpermsKind,
452 _source_sid: SecurityId,
453 _target_sid: SecurityId,
454 _target_class: ObjectClass,
455 _xperms_prefix: u8,
456 ) -> XpermsAccessDecision {
457 XpermsAccessDecision::ALLOW_ALL
458 }
459 }
460
461 impl AccessVectorComputer for DenyPermissionsAllowXperms {
462 fn access_vector_from_permissions<
463 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
464 >(
465 &self,
466 permissions: &[P],
467 ) -> Option<AccessVector> {
468 Some(access_vector_from_permissions(permissions))
469 }
470 }
471
472 #[derive(Default)]
474 struct AllowPermissionsDenyXperms;
475
476 impl Query for AllowPermissionsDenyXperms {
477 fn compute_access_decision(
478 &self,
479 _source_sid: SecurityId,
480 _target_sid: SecurityId,
481 _target_class: ObjectClass,
482 ) -> AccessDecision {
483 AccessDecision::allow(AccessVector::ALL)
484 }
485
486 fn compute_new_fs_node_sid(
487 &self,
488 _source_sid: SecurityId,
489 _target_sid: SecurityId,
490 _fs_node_class: FsNodeClass,
491 ) -> Result<SecurityId, anyhow::Error> {
492 unreachable!();
493 }
494
495 fn compute_new_fs_node_sid_with_name(
496 &self,
497 _source_sid: SecurityId,
498 _target_sid: SecurityId,
499 _fs_node_class: FsNodeClass,
500 _fs_node_name: NullessByteStr<'_>,
501 ) -> Option<SecurityId> {
502 unreachable!();
503 }
504
505 fn compute_xperms_access_decision(
506 &self,
507 _xperms_kind: XpermsKind,
508 _source_sid: SecurityId,
509 _target_sid: SecurityId,
510 _target_class: ObjectClass,
511 _xperms_prefix: u8,
512 ) -> XpermsAccessDecision {
513 XpermsAccessDecision::DENY_ALL
514 }
515 }
516
517 impl AccessVectorComputer for AllowPermissionsDenyXperms {
518 fn access_vector_from_permissions<
519 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
520 >(
521 &self,
522 permissions: &[P],
523 ) -> Option<AccessVector> {
524 Some(access_vector_from_permissions(permissions))
525 }
526 }
527
528 #[test]
529 fn has_permission_both() {
530 let deny_all: DenyAllPermissions = Default::default();
531 let allow_all: AllowAllPermissions = Default::default();
532
533 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
536 for permission in &permissions {
537 assert_eq!(
539 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
540 has_permission(
541 true,
542 &deny_all,
543 &deny_all,
544 *A_TEST_SID,
545 *A_TEST_SID,
546 permission.clone()
547 )
548 );
549 assert_eq!(
551 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
552 has_permission(
553 true,
554 &allow_all,
555 &allow_all,
556 *A_TEST_SID,
557 *A_TEST_SID,
558 permission.clone()
559 )
560 );
561 }
562 }
563
564 #[test]
565 fn has_ioctl_permission_enforcing() {
566 let deny_all: DenyAllPermissions = Default::default();
567 let allow_all: AllowAllPermissions = Default::default();
568 let deny_perms_allow_xperms: DenyPermissionsAllowXperms = Default::default();
569 let allow_perms_deny_xperms: AllowPermissionsDenyXperms = Default::default();
570 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
571
572 assert_eq!(
574 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
575 has_extended_permission(
576 true,
577 &deny_all,
578 &deny_all,
579 XpermsKind::Ioctl,
580 *A_TEST_SID,
581 *A_TEST_SID,
582 permission.clone(),
583 0xabcd
584 )
585 );
586 assert_eq!(
588 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
589 has_extended_permission(
590 true,
591 &allow_all,
592 &allow_all,
593 XpermsKind::Ioctl,
594 *A_TEST_SID,
595 *A_TEST_SID,
596 permission.clone(),
597 0xabcd
598 )
599 );
600 assert_eq!(
602 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
603 has_extended_permission(
604 true,
605 &deny_perms_allow_xperms,
606 &deny_perms_allow_xperms,
607 XpermsKind::Ioctl,
608 *A_TEST_SID,
609 *A_TEST_SID,
610 permission.clone(),
611 0xabcd
612 )
613 );
614 assert_eq!(
616 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
617 has_extended_permission(
618 true,
619 &allow_perms_deny_xperms,
620 &allow_perms_deny_xperms,
621 XpermsKind::Ioctl,
622 *A_TEST_SID,
623 *A_TEST_SID,
624 permission,
625 0xabcd
626 )
627 );
628 }
629
630 #[test]
631 fn has_ioctl_permission_not_enforcing() {
632 let deny_all: DenyAllPermissions = Default::default();
633 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
634
635 assert_eq!(
638 PermissionCheckResult { permit: true, audit: true, todo_bug: None },
639 has_extended_permission(
640 false,
641 &deny_all,
642 &deny_all,
643 XpermsKind::Ioctl,
644 *A_TEST_SID,
645 *A_TEST_SID,
646 permission,
647 0xabcd
648 )
649 );
650 }
651}