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