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