1use crate::access_vector_cache::{FifoQueryCache, Locked, Query};
6use crate::policy::{AccessVector, AccessVectorComputer, SELINUX_AVD_FLAGS_PERMISSIVE};
7use crate::security_server::SecurityServer;
8use crate::{ClassPermission, FsNodeClass, KernelPermission, NullessByteStr, SecurityId};
9
10#[cfg(target_os = "fuchsia")]
11use fuchsia_inspect_contrib::profile_duration;
12
13use std::num::NonZeroU64;
14use std::sync::Weak;
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 Locked<FifoQueryCache<Weak<SecurityServer>>>,
39}
40
41impl<'a> PermissionCheck<'a> {
42 pub(crate) fn new(
43 security_server: &'a SecurityServer,
44 access_vector_cache: &'a Locked<FifoQueryCache<Weak<SecurityServer>>>,
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_ioctl_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
81 &self,
82 source_sid: SecurityId,
83 target_sid: SecurityId,
84 permission: P,
85 ioctl: u16,
86 ) -> PermissionCheckResult {
87 has_ioctl_permission(
88 self.security_server.is_enforcing(),
89 self.access_vector_cache,
90 self.security_server,
91 source_sid,
92 target_sid,
93 permission,
94 ioctl,
95 )
96 }
97
98 pub fn security_server(&self) -> &SecurityServer {
101 self.security_server
102 }
103
104 pub fn compute_new_fs_node_sid(
109 &self,
110 source_sid: SecurityId,
111 target_sid: SecurityId,
112 fs_node_class: FsNodeClass,
113 fs_node_name: NullessByteStr<'_>,
114 ) -> Result<SecurityId, anyhow::Error> {
115 if !fs_node_name.as_bytes().is_empty() {
117 if let Some(sid) = self.access_vector_cache.compute_new_fs_node_sid_with_name(
118 source_sid,
119 target_sid,
120 fs_node_class,
121 fs_node_name,
122 ) {
123 return Ok(sid);
124 }
125 }
126 self.access_vector_cache.compute_new_fs_node_sid(source_sid, target_sid, fs_node_class)
127 }
128}
129
130fn has_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
132 is_enforcing: bool,
133 query: &impl Query,
134 access_vector_computer: &impl AccessVectorComputer,
135 source_sid: SecurityId,
136 target_sid: SecurityId,
137 permission: P,
138) -> PermissionCheckResult {
139 #[cfg(target_os = "fuchsia")]
140 profile_duration!("libselinux.check_permission");
141 let target_class = permission.class();
142
143 let decision = query.compute_access_decision(source_sid, target_sid, target_class.into());
144
145 let mut result = if let Some(permission_access_vector) =
146 access_vector_computer.access_vector_from_permissions(&[permission])
147 {
148 let permit = permission_access_vector & decision.allow == permission_access_vector;
149 let audit = if permit {
150 permission_access_vector & decision.auditallow != AccessVector::NONE
151 } else {
152 permission_access_vector & decision.auditdeny != AccessVector::NONE
153 };
154 PermissionCheckResult { permit, audit, todo_bug: None }
155 } else {
156 PermissionCheckResult { permit: false, audit: true, todo_bug: None }
157 };
158
159 if !result.permit {
160 if !is_enforcing {
161 result.permit = true;
163 } else if decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
164 result.permit = true;
167 } else if decision.todo_bug.is_some() {
168 result.permit = true;
171 result.todo_bug = decision.todo_bug;
172 }
173 }
174
175 result
176}
177
178fn has_ioctl_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
181 is_enforcing: bool,
182 query: &impl Query,
183 access_vector_computer: &impl AccessVectorComputer,
184 source_sid: SecurityId,
185 target_sid: SecurityId,
186 permission: P,
187 ioctl: u16,
188) -> PermissionCheckResult {
189 let target_class = permission.class();
190
191 let permission_decision =
192 query.compute_access_decision(source_sid, target_sid, target_class.into());
193
194 let [ioctl_postfix, ioctl_prefix] = ioctl.to_le_bytes();
195 let xperm_decision = query.compute_ioctl_access_decision(
196 source_sid,
197 target_sid,
198 target_class.into(),
199 ioctl_prefix,
200 );
201
202 let mut result = if let Some(permission_access_vector) =
203 access_vector_computer.access_vector_from_permissions(&[permission])
204 {
205 let permit = (permission_access_vector & permission_decision.allow
206 == permission_access_vector)
207 && xperm_decision.allow.contains(ioctl_postfix);
208 let audit = if permit {
209 (permission_access_vector & permission_decision.auditallow == permission_access_vector)
210 && xperm_decision.auditallow.contains(ioctl_postfix)
211 } else {
212 (permission_access_vector & permission_decision.auditdeny == permission_access_vector)
213 && xperm_decision.auditdeny.contains(ioctl_postfix)
214 };
215 PermissionCheckResult { permit, audit, todo_bug: None }
216 } else {
217 PermissionCheckResult { permit: false, audit: true, todo_bug: None }
218 };
219
220 if !result.permit {
221 if !is_enforcing {
222 result.permit = true;
223 } else if permission_decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
224 result.permit = true;
225 } else if permission_decision.todo_bug.is_some() {
226 result.permit = true;
229 result.todo_bug = permission_decision.todo_bug;
230 }
231 }
232
233 result
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::access_vector_cache::DenyAll;
240 use crate::policy::testing::{ACCESS_VECTOR_0001, ACCESS_VECTOR_0010};
241 use crate::policy::{AccessDecision, AccessVector, IoctlAccessDecision};
242 use crate::{
243 CommonFilePermission, CommonFsNodePermission, FileClass, FilePermission, ObjectClass,
244 ProcessPermission,
245 };
246
247 use std::num::NonZeroU32;
248 use std::sync::atomic::{AtomicU32, Ordering};
249 use std::sync::LazyLock;
250
251 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
253
254 fn unique_sid() -> SecurityId {
256 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
257 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
258 }
259
260 fn access_vector_from_permission<P: ClassPermission + Into<KernelPermission> + 'static>(
261 permission: P,
262 ) -> AccessVector {
263 match permission.into() {
264 KernelPermission::Process(ProcessPermission::Fork) => ACCESS_VECTOR_0001,
266 KernelPermission::Process(ProcessPermission::Transition) => ACCESS_VECTOR_0010,
267 KernelPermission::File(FilePermission::Common(CommonFilePermission::Common(
269 CommonFsNodePermission::Ioctl,
270 ))) => ACCESS_VECTOR_0001,
271 _ => AccessVector::NONE,
272 }
273 }
274
275 fn access_vector_from_permissions<
276 'a,
277 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
278 >(
279 permissions: &[P],
280 ) -> AccessVector {
281 let mut access_vector = AccessVector::NONE;
282 for permission in permissions {
283 access_vector |= access_vector_from_permission(permission.clone());
284 }
285 access_vector
286 }
287
288 #[derive(Default)]
289 pub struct DenyAllPermissions(DenyAll);
290
291 impl Query for DenyAllPermissions {
292 fn compute_access_decision(
293 &self,
294 source_sid: SecurityId,
295 target_sid: SecurityId,
296 target_class: ObjectClass,
297 ) -> AccessDecision {
298 self.0.compute_access_decision(source_sid, target_sid, target_class)
299 }
300
301 fn compute_new_fs_node_sid(
302 &self,
303 _source_sid: SecurityId,
304 _target_sid: SecurityId,
305 _fs_node_class: FsNodeClass,
306 ) -> Result<SecurityId, anyhow::Error> {
307 unreachable!();
308 }
309
310 fn compute_new_fs_node_sid_with_name(
311 &self,
312 _source_sid: SecurityId,
313 _target_sid: SecurityId,
314 _fs_node_class: FsNodeClass,
315 _fs_node_name: NullessByteStr<'_>,
316 ) -> Option<SecurityId> {
317 unreachable!();
318 }
319
320 fn compute_ioctl_access_decision(
321 &self,
322 source_sid: SecurityId,
323 target_sid: SecurityId,
324 target_class: ObjectClass,
325 ioctl_prefix: u8,
326 ) -> IoctlAccessDecision {
327 self.0.compute_ioctl_access_decision(source_sid, target_sid, target_class, ioctl_prefix)
328 }
329 }
330
331 impl AccessVectorComputer for DenyAllPermissions {
332 fn access_vector_from_permissions<
333 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
334 >(
335 &self,
336 permissions: &[P],
337 ) -> Option<AccessVector> {
338 Some(access_vector_from_permissions(permissions))
339 }
340 }
341
342 #[derive(Default)]
344 struct AllowAllPermissions;
345
346 impl Query for AllowAllPermissions {
347 fn compute_access_decision(
348 &self,
349 _source_sid: SecurityId,
350 _target_sid: SecurityId,
351 _target_class: ObjectClass,
352 ) -> AccessDecision {
353 AccessDecision::allow(AccessVector::ALL)
354 }
355
356 fn compute_new_fs_node_sid(
357 &self,
358 _source_sid: SecurityId,
359 _target_sid: SecurityId,
360 _fs_node_class: FsNodeClass,
361 ) -> Result<SecurityId, anyhow::Error> {
362 unreachable!();
363 }
364
365 fn compute_new_fs_node_sid_with_name(
366 &self,
367 _source_sid: SecurityId,
368 _target_sid: SecurityId,
369 _fs_node_class: FsNodeClass,
370 _fs_node_name: NullessByteStr<'_>,
371 ) -> Option<SecurityId> {
372 unreachable!();
373 }
374
375 fn compute_ioctl_access_decision(
376 &self,
377 _source_sid: SecurityId,
378 _target_sid: SecurityId,
379 _target_class: ObjectClass,
380 _ioctl_prefix: u8,
381 ) -> IoctlAccessDecision {
382 IoctlAccessDecision::ALLOW_ALL
383 }
384 }
385
386 impl AccessVectorComputer for AllowAllPermissions {
387 fn access_vector_from_permissions<
388 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
389 >(
390 &self,
391 permissions: &[P],
392 ) -> Option<AccessVector> {
393 Some(access_vector_from_permissions(permissions))
394 }
395 }
396
397 #[derive(Default)]
399 struct DenyPermissionsAllowXperms;
400
401 impl Query for DenyPermissionsAllowXperms {
402 fn compute_access_decision(
403 &self,
404 _source_sid: SecurityId,
405 _target_sid: SecurityId,
406 _target_class: ObjectClass,
407 ) -> AccessDecision {
408 AccessDecision::allow(AccessVector::NONE)
409 }
410
411 fn compute_new_fs_node_sid(
412 &self,
413 _source_sid: SecurityId,
414 _target_sid: SecurityId,
415 _fs_node_class: FsNodeClass,
416 ) -> Result<SecurityId, anyhow::Error> {
417 unreachable!();
418 }
419
420 fn compute_new_fs_node_sid_with_name(
421 &self,
422 _source_sid: SecurityId,
423 _target_sid: SecurityId,
424 _fs_node_class: FsNodeClass,
425 _fs_node_name: NullessByteStr<'_>,
426 ) -> Option<SecurityId> {
427 unreachable!();
428 }
429
430 fn compute_ioctl_access_decision(
431 &self,
432 _source_sid: SecurityId,
433 _target_sid: SecurityId,
434 _target_class: ObjectClass,
435 _ioctl_prefix: u8,
436 ) -> IoctlAccessDecision {
437 IoctlAccessDecision::ALLOW_ALL
438 }
439 }
440
441 impl AccessVectorComputer for DenyPermissionsAllowXperms {
442 fn access_vector_from_permissions<
443 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
444 >(
445 &self,
446 permissions: &[P],
447 ) -> Option<AccessVector> {
448 Some(access_vector_from_permissions(permissions))
449 }
450 }
451
452 #[derive(Default)]
454 struct AllowPermissionsDenyXperms;
455
456 impl Query for AllowPermissionsDenyXperms {
457 fn compute_access_decision(
458 &self,
459 _source_sid: SecurityId,
460 _target_sid: SecurityId,
461 _target_class: ObjectClass,
462 ) -> AccessDecision {
463 AccessDecision::allow(AccessVector::ALL)
464 }
465
466 fn compute_new_fs_node_sid(
467 &self,
468 _source_sid: SecurityId,
469 _target_sid: SecurityId,
470 _fs_node_class: FsNodeClass,
471 ) -> Result<SecurityId, anyhow::Error> {
472 unreachable!();
473 }
474
475 fn compute_new_fs_node_sid_with_name(
476 &self,
477 _source_sid: SecurityId,
478 _target_sid: SecurityId,
479 _fs_node_class: FsNodeClass,
480 _fs_node_name: NullessByteStr<'_>,
481 ) -> Option<SecurityId> {
482 unreachable!();
483 }
484
485 fn compute_ioctl_access_decision(
486 &self,
487 _source_sid: SecurityId,
488 _target_sid: SecurityId,
489 _target_class: ObjectClass,
490 _ioctl_prefix: u8,
491 ) -> IoctlAccessDecision {
492 IoctlAccessDecision::DENY_ALL
493 }
494 }
495
496 impl AccessVectorComputer for AllowPermissionsDenyXperms {
497 fn access_vector_from_permissions<
498 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
499 >(
500 &self,
501 permissions: &[P],
502 ) -> Option<AccessVector> {
503 Some(access_vector_from_permissions(permissions))
504 }
505 }
506
507 #[test]
508 fn has_permission_both() {
509 let deny_all: DenyAllPermissions = Default::default();
510 let allow_all: AllowAllPermissions = Default::default();
511
512 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
515 for permission in &permissions {
516 assert_eq!(
518 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
519 has_permission(
520 true,
521 &deny_all,
522 &deny_all,
523 *A_TEST_SID,
524 *A_TEST_SID,
525 permission.clone()
526 )
527 );
528 assert_eq!(
530 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
531 has_permission(
532 true,
533 &allow_all,
534 &allow_all,
535 *A_TEST_SID,
536 *A_TEST_SID,
537 permission.clone()
538 )
539 );
540 }
541 }
542
543 #[test]
544 fn has_ioctl_permission_enforcing() {
545 let deny_all: DenyAllPermissions = Default::default();
546 let allow_all: AllowAllPermissions = Default::default();
547 let deny_perms_allow_xperms: DenyPermissionsAllowXperms = Default::default();
548 let allow_perms_deny_xperms: AllowPermissionsDenyXperms = Default::default();
549 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
550
551 assert_eq!(
553 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
554 has_ioctl_permission(
555 true,
556 &deny_all,
557 &deny_all,
558 *A_TEST_SID,
559 *A_TEST_SID,
560 permission.clone(),
561 0xabcd
562 )
563 );
564 assert_eq!(
566 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
567 has_ioctl_permission(
568 true,
569 &allow_all,
570 &allow_all,
571 *A_TEST_SID,
572 *A_TEST_SID,
573 permission.clone(),
574 0xabcd
575 )
576 );
577 assert_eq!(
579 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
580 has_ioctl_permission(
581 true,
582 &deny_perms_allow_xperms,
583 &deny_perms_allow_xperms,
584 *A_TEST_SID,
585 *A_TEST_SID,
586 permission.clone(),
587 0xabcd
588 )
589 );
590 assert_eq!(
592 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
593 has_ioctl_permission(
594 true,
595 &allow_perms_deny_xperms,
596 &allow_perms_deny_xperms,
597 *A_TEST_SID,
598 *A_TEST_SID,
599 permission,
600 0xabcd
601 )
602 );
603 }
604
605 #[test]
606 fn has_ioctl_permission_not_enforcing() {
607 let deny_all: DenyAllPermissions = Default::default();
608 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
609
610 assert_eq!(
613 PermissionCheckResult { permit: true, audit: true, todo_bug: None },
614 has_ioctl_permission(
615 false,
616 &deny_all,
617 &deny_all,
618 *A_TEST_SID,
619 *A_TEST_SID,
620 permission,
621 0xabcd
622 )
623 );
624 }
625}