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 decision.todo_bug.is_some() {
175 result.permit = true;
182 result.todo_bug = decision.todo_bug;
183 } else if !is_enforcing {
184 result.permit = true;
186 } else if decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
187 result.permit = true;
190 }
191 }
192
193 result
194}
195
196fn has_ioctl_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
199 is_enforcing: bool,
200 query: &impl Query,
201 access_vector_computer: &impl AccessVectorComputer,
202 source_sid: SecurityId,
203 target_sid: SecurityId,
204 permission: P,
205 ioctl: 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 [ioctl_postfix, ioctl_prefix] = ioctl.to_le_bytes();
213 let xperm_decision = query.compute_ioctl_access_decision(
214 source_sid,
215 target_sid,
216 target_class.into(),
217 ioctl_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 && xperm_decision.allow.contains(ioctl_postfix);
226 let audit = if permit {
227 (permission_access_vector & permission_decision.auditallow == permission_access_vector)
228 && xperm_decision.auditallow.contains(ioctl_postfix)
229 } else {
230 (permission_access_vector & permission_decision.auditdeny == permission_access_vector)
231 && xperm_decision.auditdeny.contains(ioctl_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, IoctlAccessDecision};
259 use crate::{
260 CommonFilePermission, CommonFsNodePermission, FileClass, FilePermission, ObjectClass,
261 ProcessPermission,
262 };
263
264 use std::num::NonZeroU32;
265 use std::sync::atomic::{AtomicU32, Ordering};
266 use std::sync::LazyLock;
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_ioctl_access_decision(
338 &self,
339 _source_sid: SecurityId,
340 _target_sid: SecurityId,
341 _target_class: ObjectClass,
342 _ioctl_prefix: u8,
343 ) -> IoctlAccessDecision {
344 IoctlAccessDecision::DENY_ALL
345 }
346 }
347
348 impl AccessVectorComputer for DenyAllPermissions {
349 fn access_vector_from_permissions<
350 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
351 >(
352 &self,
353 permissions: &[P],
354 ) -> Option<AccessVector> {
355 Some(access_vector_from_permissions(permissions))
356 }
357 }
358
359 #[derive(Default)]
361 struct AllowAllPermissions;
362
363 impl Query for AllowAllPermissions {
364 fn compute_access_decision(
365 &self,
366 _source_sid: SecurityId,
367 _target_sid: SecurityId,
368 _target_class: ObjectClass,
369 ) -> AccessDecision {
370 AccessDecision::allow(AccessVector::ALL)
371 }
372
373 fn compute_new_fs_node_sid(
374 &self,
375 _source_sid: SecurityId,
376 _target_sid: SecurityId,
377 _fs_node_class: FsNodeClass,
378 ) -> Result<SecurityId, anyhow::Error> {
379 unreachable!();
380 }
381
382 fn compute_new_fs_node_sid_with_name(
383 &self,
384 _source_sid: SecurityId,
385 _target_sid: SecurityId,
386 _fs_node_class: FsNodeClass,
387 _fs_node_name: NullessByteStr<'_>,
388 ) -> Option<SecurityId> {
389 unreachable!();
390 }
391
392 fn compute_ioctl_access_decision(
393 &self,
394 _source_sid: SecurityId,
395 _target_sid: SecurityId,
396 _target_class: ObjectClass,
397 _ioctl_prefix: u8,
398 ) -> IoctlAccessDecision {
399 IoctlAccessDecision::ALLOW_ALL
400 }
401 }
402
403 impl AccessVectorComputer for AllowAllPermissions {
404 fn access_vector_from_permissions<
405 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
406 >(
407 &self,
408 permissions: &[P],
409 ) -> Option<AccessVector> {
410 Some(access_vector_from_permissions(permissions))
411 }
412 }
413
414 #[derive(Default)]
416 struct DenyPermissionsAllowXperms;
417
418 impl Query for DenyPermissionsAllowXperms {
419 fn compute_access_decision(
420 &self,
421 _source_sid: SecurityId,
422 _target_sid: SecurityId,
423 _target_class: ObjectClass,
424 ) -> AccessDecision {
425 AccessDecision::allow(AccessVector::NONE)
426 }
427
428 fn compute_new_fs_node_sid(
429 &self,
430 _source_sid: SecurityId,
431 _target_sid: SecurityId,
432 _fs_node_class: FsNodeClass,
433 ) -> Result<SecurityId, anyhow::Error> {
434 unreachable!();
435 }
436
437 fn compute_new_fs_node_sid_with_name(
438 &self,
439 _source_sid: SecurityId,
440 _target_sid: SecurityId,
441 _fs_node_class: FsNodeClass,
442 _fs_node_name: NullessByteStr<'_>,
443 ) -> Option<SecurityId> {
444 unreachable!();
445 }
446
447 fn compute_ioctl_access_decision(
448 &self,
449 _source_sid: SecurityId,
450 _target_sid: SecurityId,
451 _target_class: ObjectClass,
452 _ioctl_prefix: u8,
453 ) -> IoctlAccessDecision {
454 IoctlAccessDecision::ALLOW_ALL
455 }
456 }
457
458 impl AccessVectorComputer for DenyPermissionsAllowXperms {
459 fn access_vector_from_permissions<
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_new_fs_node_sid(
484 &self,
485 _source_sid: SecurityId,
486 _target_sid: SecurityId,
487 _fs_node_class: FsNodeClass,
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_ioctl_access_decision(
503 &self,
504 _source_sid: SecurityId,
505 _target_sid: SecurityId,
506 _target_class: ObjectClass,
507 _ioctl_prefix: u8,
508 ) -> IoctlAccessDecision {
509 IoctlAccessDecision::DENY_ALL
510 }
511 }
512
513 impl AccessVectorComputer for AllowPermissionsDenyXperms {
514 fn access_vector_from_permissions<
515 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
516 >(
517 &self,
518 permissions: &[P],
519 ) -> Option<AccessVector> {
520 Some(access_vector_from_permissions(permissions))
521 }
522 }
523
524 #[test]
525 fn has_permission_both() {
526 let deny_all: DenyAllPermissions = Default::default();
527 let allow_all: AllowAllPermissions = Default::default();
528
529 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
532 for permission in &permissions {
533 assert_eq!(
535 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
536 has_permission(
537 true,
538 &deny_all,
539 &deny_all,
540 *A_TEST_SID,
541 *A_TEST_SID,
542 permission.clone()
543 )
544 );
545 assert_eq!(
547 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
548 has_permission(
549 true,
550 &allow_all,
551 &allow_all,
552 *A_TEST_SID,
553 *A_TEST_SID,
554 permission.clone()
555 )
556 );
557 }
558 }
559
560 #[test]
561 fn has_ioctl_permission_enforcing() {
562 let deny_all: DenyAllPermissions = Default::default();
563 let allow_all: AllowAllPermissions = Default::default();
564 let deny_perms_allow_xperms: DenyPermissionsAllowXperms = Default::default();
565 let allow_perms_deny_xperms: AllowPermissionsDenyXperms = Default::default();
566 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
567
568 assert_eq!(
570 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
571 has_ioctl_permission(
572 true,
573 &deny_all,
574 &deny_all,
575 *A_TEST_SID,
576 *A_TEST_SID,
577 permission.clone(),
578 0xabcd
579 )
580 );
581 assert_eq!(
583 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
584 has_ioctl_permission(
585 true,
586 &allow_all,
587 &allow_all,
588 *A_TEST_SID,
589 *A_TEST_SID,
590 permission.clone(),
591 0xabcd
592 )
593 );
594 assert_eq!(
596 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
597 has_ioctl_permission(
598 true,
599 &deny_perms_allow_xperms,
600 &deny_perms_allow_xperms,
601 *A_TEST_SID,
602 *A_TEST_SID,
603 permission.clone(),
604 0xabcd
605 )
606 );
607 assert_eq!(
609 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
610 has_ioctl_permission(
611 true,
612 &allow_perms_deny_xperms,
613 &allow_perms_deny_xperms,
614 *A_TEST_SID,
615 *A_TEST_SID,
616 permission,
617 0xabcd
618 )
619 );
620 }
621
622 #[test]
623 fn has_ioctl_permission_not_enforcing() {
624 let deny_all: DenyAllPermissions = Default::default();
625 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
626
627 assert_eq!(
630 PermissionCheckResult { permit: true, audit: true, todo_bug: None },
631 has_ioctl_permission(
632 false,
633 &deny_all,
634 &deny_all,
635 *A_TEST_SID,
636 *A_TEST_SID,
637 permission,
638 0xabcd
639 )
640 );
641 }
642}