Skip to main content

selinux/policy/
mod.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod arrays;
6pub mod error;
7pub mod index;
8pub mod parsed_policy;
9pub mod parser;
10pub mod view;
11
12mod constraints;
13mod security_context;
14
15pub use crate::new_policy::{
16    AccessDecision, AccessVectorRules, IndexedAccessVectorRules, SELINUX_AVD_FLAGS_PERMISSIVE,
17    XpermsBitmap,
18};
19pub use arrays::FsUseType;
20pub use index::FsUseLabelAndType;
21pub use parser::PolicyCursor;
22pub use security_context::{SecurityContext, SecurityContextError};
23
24use crate::new_policy::traits::Serialize as _;
25pub use crate::new_policy::traits::{HasName, HasPolicyId, PolicyId};
26pub use crate::new_policy::{
27    AccessVector, CategoryId, ClassId, HandleUnknown, MlsLevel, MlsRange, POLICYDB_VERSION_MAX,
28    PermissionId, RoleId, SensitivityId, TypeId, User, UserId,
29};
30use crate::{ClassPermission, KernelClass, NullessByteStr, ObjectClass, new_policy as new};
31use index::PolicyIndex;
32use parsed_policy::ParsedPolicy;
33use parser::PolicyData;
34
35use anyhow::Context as _;
36use std::fmt::Debug;
37use std::num::NonZeroU32;
38use std::ops::Deref;
39
40use std::sync::Arc;
41use zerocopy::{
42    FromBytes, Immutable, KnownLayout, Ref, SplitByteSlice, Unaligned, little_endian as le,
43};
44
45impl<T, Tag> Parse for crate::new_policy::IdType<T, Tag>
46where
47    crate::new_policy::IdType<T, Tag>: crate::new_policy::traits::PolicyId,
48{
49    type Error = error::ParseError;
50
51    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
52        let (id_val, tail) = PolicyCursor::parse::<le::U32>(bytes)?;
53        let id = Self::try_from(id_val.get())
54            .map_err(|_| error::ParseError::InvalidId { value: id_val.get() })?;
55        Ok((id, tail))
56    }
57}
58
59impl<T, Tag> Validate for crate::new_policy::IdType<T, Tag>
60where
61    crate::new_policy::IdType<T, Tag>: crate::new_policy::traits::PolicyId,
62{
63    type Error = anyhow::Error;
64
65    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
66        Ok(())
67    }
68}
69
70/// Encapsulates the result of a permissions calculation, between
71/// source & target domains, for a specific class. Decisions describe
72
73/// A kind of extended permission, corresponding to the base permission that should trigger a check
74/// of an extended permission.
75#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
76pub enum XpermsKind {
77    Ioctl,
78    Nlmsg,
79}
80
81/// Encapsulates the result of an extended permissions calculation, between source & target
82/// domains, for a specific class, a specific kind of extended permissions, and for a specific
83/// xperm prefix byte. Decisions describe which 16-bit xperms are allowed, and whether xperms
84/// should be audit-logged when allowed, and when denied.
85#[derive(Debug, Clone, PartialEq)]
86pub struct XpermsAccessDecision {
87    pub allow: XpermsBitmap,
88    pub auditallow: XpermsBitmap,
89    pub auditdeny: XpermsBitmap,
90}
91
92impl XpermsAccessDecision {
93    pub const DENY_ALL: Self = Self {
94        allow: XpermsBitmap::NONE,
95        auditallow: XpermsBitmap::NONE,
96        auditdeny: XpermsBitmap::ALL,
97    };
98    pub const ALLOW_ALL: Self = Self {
99        allow: XpermsBitmap::ALL,
100        auditallow: XpermsBitmap::NONE,
101        auditdeny: XpermsBitmap::ALL,
102    };
103}
104
105/// Parses `binary_policy` by value; that is, copies underlying binary data out in addition to
106/// building up parser output structures. This function returns
107/// `(unvalidated_parser_output, binary_policy)` on success, or an error if parsing failed. Note
108/// that the second component of the success case contains precisely the same bytes as the input.
109/// This function depends on a uniformity of interface between the "by value" and "by reference"
110/// strategies, but also requires an `unvalidated_parser_output` type that is independent of the
111/// `binary_policy` lifetime. Taken together, these requirements demand the "move-in + move-out"
112/// interface for `binary_policy`.
113pub fn parse_policy_by_value(binary_policy: Vec<u8>) -> Result<Unvalidated, anyhow::Error> {
114    let policy_data: PolicyData = Arc::from(binary_policy);
115    let policy = ParsedPolicy::parse(policy_data).context("parsing policy")?;
116    Ok(Unvalidated(policy))
117}
118
119#[derive(Debug)]
120pub struct Policy(PolicyIndex);
121
122impl Deref for Policy {
123    type Target = PolicyIndex;
124
125    fn deref(&self) -> &Self::Target {
126        &self.0
127    }
128}
129
130impl Policy {
131    /// Serializes the policy back into [`PolicyData`].
132    pub fn serialize(&self) -> PolicyData {
133        let mut bytes = Vec::new();
134        self.0.serialize(&mut bytes).expect("serialization of new_policy should succeed");
135        std::sync::Arc::from(bytes)
136    }
137
138    pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
139        self.0
140            .conditional_booleans()
141            .iter()
142            .map(|boolean| (boolean.name(), boolean.active()))
143            .collect()
144    }
145
146    /// Returns the set of permissions for the given class, including both the
147    /// explicitly owned permissions and the inherited ones from common symbols.
148    /// Each permission is a tuple of the permission identifier (in the scope of
149    /// the given class) and the permission name.
150    pub fn find_class_permissions_by_name(
151        &self,
152        class_name: &str,
153    ) -> Result<Vec<(PermissionId, Vec<u8>)>, ()> {
154        let classes = self.classes();
155        let class = classes.get_by_name(class_name.as_bytes()).ok_or(())?;
156        let owned_permissions = class.permissions();
157
158        let mut result: Vec<_> = owned_permissions
159            .iter()
160            .map(|permission| (permission.id(), permission.name().to_vec()))
161            .collect();
162
163        // common_name() is empty when the class doesn't inherit from a CommonSymbol.
164        if class.common_name().is_empty() {
165            return Ok(result);
166        }
167
168        let common_symbol_permissions =
169            self.common_symbols().get_by_name(class.common_name()).ok_or(())?.permissions();
170
171        result.append(
172            &mut common_symbol_permissions
173                .iter()
174                .map(|permission| (permission.id(), permission.name().to_vec()))
175                .collect(),
176        );
177
178        Ok(result)
179    }
180
181    /// If there is an fs_use statement for the given filesystem type, returns the associated
182    /// [`SecurityContext`] and [`FsUseType`].
183    pub fn fs_use_label_and_type(&self, fs_type: NullessByteStr<'_>) -> Option<FsUseLabelAndType> {
184        self.0.fs_use_label_and_type(fs_type)
185    }
186
187    /// If there is a genfscon statement for the given filesystem type, returns the associated
188    /// [`SecurityContext`].
189    pub fn genfscon_label_for_fs_and_path(
190        &self,
191        fs_type: NullessByteStr<'_>,
192        node_path: NullessByteStr<'_>,
193        class_id: Option<KernelClass>,
194    ) -> Option<SecurityContext> {
195        self.0.genfscon_label_for_fs_and_path(fs_type, node_path, class_id)
196    }
197
198    /// Returns the [`SecurityContext`] defined by this policy for the specified
199    /// well-known (or "initial") Id.
200    pub fn initial_context(&self, id: crate::InitialSid) -> security_context::SecurityContext {
201        self.0.initial_context(id)
202    }
203
204    /// Returns a [`SecurityContext`] with fields parsed from the supplied Security Context string.
205    pub fn parse_security_context(
206        &self,
207        security_context: NullessByteStr<'_>,
208    ) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
209        security_context::SecurityContext::from_string(&self.0, security_context)
210    }
211
212    /// Validates a [`SecurityContext`] against this policy's constraints.
213    pub fn validate_security_context(
214        &self,
215        security_context: &SecurityContext,
216    ) -> Result<(), SecurityContextError> {
217        security_context.validate(&self.0)
218    }
219
220    /// Returns a byte string describing the supplied [`SecurityContext`].
221    pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
222        security_context.to_string(&self.0)
223    }
224
225    /// Returns the security context that should be applied to a newly created SELinux
226    /// object according to `source` and `target` security contexts, as well as the new object's
227    /// `class`.
228    ///
229    /// If no filename-transition rule matches the supplied arguments then
230    /// `None` is returned, and the caller should fall-back to filename-independent labeling
231    /// via [`compute_create_context()`]
232    pub fn compute_create_context_with_name(
233        &self,
234        source: &SecurityContext,
235        target: &SecurityContext,
236        class: impl Into<ObjectClass>,
237        name: NullessByteStr<'_>,
238    ) -> Option<SecurityContext> {
239        self.0.compute_create_context_with_name(source, target, class.into(), name)
240    }
241
242    /// Returns the security context that should be applied to a newly created SELinux
243    /// object according to `source` and `target` security contexts, as well as the new object's
244    /// `class`.
245    ///
246    /// Computation follows the "create" algorithm for labeling newly created objects:
247    /// - user is taken from the `source` by default, or `target` if specified by policy.
248    /// - role, type and range are taken from the matching transition rules, if any.
249    /// - role, type and range fall-back to the `source` or `target` values according to policy.
250    ///
251    /// If no transitions apply, and the policy does not explicitly specify defaults then the
252    /// role, type and range values have defaults chosen based on the `class`:
253    /// - For "process", and socket-like classes, role, type and range are taken from the `source`.
254    /// - Otherwise role is "object_r", type is taken from `target` and range is set to the
255    ///   low level of the `source` range.
256    ///
257    /// Returns an error if the Security Context for such an object is not valid under this
258    /// [`Policy`] (e.g. if the type is not permitted for the chosen role, etc).
259    pub fn compute_create_context(
260        &self,
261        source: &SecurityContext,
262        target: &SecurityContext,
263        class: impl Into<ObjectClass>,
264    ) -> SecurityContext {
265        self.0.compute_create_context(source, target, class.into())
266    }
267
268    /// Computes the access vector that associates type `source_type_name` and
269    /// `target_type_name` via an explicit `allow [...];` statement in the
270    /// binary policy, subject to any matching constraint statements. Computes
271    /// `AccessVector::NONE` if no such statement exists.
272    ///
273    /// Access decisions are currently based on explicit "allow" rules and
274    /// "constrain" or "mlsconstrain" statements. A permission is allowed if
275    /// it is allowed by an explicit "allow", and if in addition, all matching
276    /// constraints are satisfied.
277    pub fn compute_access_decision(
278        &self,
279        source_context: &SecurityContext,
280        target_context: &SecurityContext,
281        object_class: impl Into<ObjectClass>,
282    ) -> AccessDecision {
283        if let Some(target_class) = self.0.class(object_class.into()) {
284            self.0.compute_access_decision(source_context, target_context, &target_class)
285        } else {
286            let mut decision = AccessDecision::allow(AccessVector::NONE);
287            if self.is_permissive(source_context.type_()) {
288                decision.flags |= SELINUX_AVD_FLAGS_PERMISSIVE;
289            }
290            decision
291        }
292    }
293
294    /// Computes the extended permissions that should be allowed, audited when allowed, and audited
295    /// when denied, for a given kind of extended permissions (`ioctl` or `nlmsg`), source context,
296    /// target context, target class, and xperms prefix byte.
297    pub fn compute_xperms_access_decision(
298        &self,
299        xperms_kind: XpermsKind,
300        source_context: &SecurityContext,
301        target_context: &SecurityContext,
302        object_class: impl Into<ObjectClass>,
303        xperms_prefix: u8,
304    ) -> XpermsAccessDecision {
305        if let Some(target_class) = self.0.class(object_class.into()) {
306            self.0.compute_xperms_access_decision(
307                xperms_kind,
308                source_context,
309                target_context,
310                &target_class,
311                xperms_prefix,
312            )
313        } else {
314            XpermsAccessDecision::DENY_ALL
315        }
316    }
317
318    pub fn is_bounded_by(&self, bounded_type: TypeId, parent_type: TypeId) -> bool {
319        self.0.types().get_by_id(bounded_type).unwrap().bounded_by() == Some(parent_type)
320    }
321
322    /// Returns true if the policy has the marked the type/domain for permissive checks.
323    pub fn is_permissive(&self, type_: TypeId) -> bool {
324        self.0.permissive_map().contains(type_)
325    }
326}
327
328impl AccessVectorComputer for Policy {
329    fn access_decision_to_kernel_access_decision(
330        &self,
331        class: KernelClass,
332        av: AccessDecision,
333    ) -> KernelAccessDecision {
334        let mut kernel_allow;
335        let mut kernel_audit;
336        // Set the default values of the bits as appropriate for the policy's handle_unknown value.
337        // Bits corresponding to policy-known permissions will be overwritten.
338        if self.0.handle_unknown() == HandleUnknown::Allow {
339            // If we allow unknown permissions, a bit will be by default allowed and not audited.
340            kernel_allow = 0xffffffffu32;
341            kernel_audit = 0u32;
342        } else {
343            // Otherwise, a bit is by default audited and not allowed.
344            kernel_allow = 0u32;
345            kernel_audit = 0xffffffffu32;
346        }
347
348        let decision_allow = av.allow;
349        let decision_audit = (av.allow & av.auditallow) | (!av.allow & av.auditdeny);
350        for permission in class.permissions() {
351            if let Some(permission_access_vector) =
352                self.0.kernel_permission_to_access_vector(permission.clone())
353            {
354                // If the permission is known, set the corresponding bit according to
355                // `decision_allow` and `decision_audit`.
356                let bit = 1 << permission.id();
357                let allow = decision_allow & permission_access_vector == permission_access_vector;
358                let audit = decision_audit & permission_access_vector == permission_access_vector;
359                kernel_allow = (kernel_allow & !bit) | ((allow as u32) << permission.id());
360                kernel_audit = (kernel_audit & !bit) | ((audit as u32) << permission.id());
361            }
362        }
363        KernelAccessDecision {
364            allow: AccessVector::from(kernel_allow),
365            audit: AccessVector::from(kernel_audit),
366            flags: av.flags,
367            todo_bug: av.todo_bug,
368        }
369    }
370}
371
372/// A [`Policy`] that has been successfully parsed, but not validated.
373pub struct Unvalidated(ParsedPolicy);
374
375impl Unvalidated {
376    pub fn validate(self) -> Result<Policy, anyhow::Error> {
377        self.0.validate().context("validating parsed policy")?;
378        let index = PolicyIndex::new(self.0).context("building index")?;
379        Ok(Policy(index))
380    }
381}
382
383#[derive(Clone, Copy, Debug, PartialEq, Eq)]
384pub struct KernelAccessDecision {
385    pub allow: AccessVector,
386    pub audit: AccessVector,
387    pub flags: u32,
388    pub todo_bug: Option<NonZeroU32>,
389}
390
391/// An owner of policy information that can translate [`crate::Permission`] values into
392/// [`AccessVector`] values that are consistent with the owned policy.
393pub trait AccessVectorComputer {
394    /// Translates the given [`AccessDecision`] to a [`KernelAccessDecision`].
395    ///
396    /// The loaded policy's "handle unknown" configuration determines how `permissions`
397    /// entries not explicitly defined by the policy are handled. Allow-unknown will
398    /// result in unknown `permissions` being allowed, while they are denied (and audited)
399    /// if the policy uses deny-unknown.
400    fn access_decision_to_kernel_access_decision(
401        &self,
402        class: KernelClass,
403        av: AccessDecision,
404    ) -> KernelAccessDecision;
405}
406
407/// A data structure that can be parsed as a part of a binary policy.
408pub trait Parse: Sized {
409    /// The type of error that may be returned from `parse()`, usually [`ParseError`] or
410    /// [`anyhow::Error`].
411    type Error: Into<anyhow::Error>;
412
413    /// Parses a `Self` from `bytes`, returning the `Self` and trailing bytes, or an error if
414    /// bytes corresponding to a `Self` are malformed.
415    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error>;
416}
417
418/// Context for validating a parsed policy.
419pub(super) struct PolicyValidationContext {
420    /// Policy data that is being validated.
421    pub(super) data: PolicyData,
422
423    /// True if "userspace_initial_context" is enabled, which requires the "init" SID to be defined.
424    pub(super) need_init_sid: bool,
425
426    /// New policy parser representation.
427    pub(super) new_policy: Arc<new::NewPolicy>,
428}
429
430/// Validate a parsed data structure.
431pub(super) trait Validate {
432    /// The type of error that may be returned from `validate()`, usually [`ParseError`] or
433    /// [`anyhow::Error`].
434    type Error: Into<anyhow::Error>;
435
436    /// Validates a `Self`, returning a `Self::Error` if `self` is internally inconsistent.
437    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error>;
438}
439
440/// Treat a type as metadata that contains a count of subsequent data.
441pub(super) trait Counted {
442    /// Returns the count of subsequent data items.
443    fn count(&self) -> u32;
444}
445
446impl<T: Validate> Validate for Option<T> {
447    type Error = <T as Validate>::Error;
448
449    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
450        match self {
451            Some(value) => value.validate(context),
452            None => Ok(()),
453        }
454    }
455}
456
457impl<T: Validate> Validate for Vec<T> {
458    type Error = <T as Validate>::Error;
459
460    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
461        for item in self {
462            item.validate(context)?;
463        }
464        Ok(())
465    }
466}
467
468impl Validate for le::U32 {
469    type Error = anyhow::Error;
470
471    /// Using a raw `le::U32` implies no additional constraints on its value. To operate with
472    /// constraints, define a `struct T(le::U32);` and `impl Validate for T { ... }`.
473    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
474        Ok(())
475    }
476}
477
478impl Validate for u8 {
479    type Error = anyhow::Error;
480
481    /// Using a raw `u8` implies no additional constraints on its value. To operate with
482    /// constraints, define a `struct T(u8);` and `impl Validate for T { ... }`.
483    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
484        Ok(())
485    }
486}
487
488impl<B: SplitByteSlice, T: Validate + FromBytes + KnownLayout + Immutable> Validate for Ref<B, T> {
489    type Error = <T as Validate>::Error;
490
491    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
492        self.deref().validate(context)
493    }
494}
495
496impl<B: SplitByteSlice, T: Counted + FromBytes + KnownLayout + Immutable> Counted for Ref<B, T> {
497    fn count(&self) -> u32 {
498        self.deref().count()
499    }
500}
501
502/// A length-encoded array that contains metadata of type `M` and a vector of data items of type `T`.
503#[derive(Clone, Debug, PartialEq)]
504struct Array<M, T> {
505    metadata: M,
506    data: Vec<T>,
507}
508
509impl<M: Counted + Parse, T: Parse> Parse for Array<M, T> {
510    /// [`Array`] abstracts over two types (`M` and `D`) that may have different [`Parse::Error`]
511    /// types. Unify error return type via [`anyhow::Error`].
512    type Error = anyhow::Error;
513
514    /// Parses [`Array`] by parsing *and validating* `metadata`, `data`, and `self`.
515    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
516        let tail = bytes;
517
518        let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
519
520        let count = metadata.count() as usize;
521        let mut data = Vec::with_capacity(count);
522        let mut cur_tail = tail;
523        for _ in 0..count {
524            let (item, next_tail) = T::parse(cur_tail).map_err(Into::<anyhow::Error>::into)?;
525            data.push(item);
526            cur_tail = next_tail;
527        }
528        let tail = cur_tail;
529
530        let array = Self { metadata, data };
531
532        Ok((array, tail))
533    }
534}
535
536impl<T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned> Parse for T {
537    type Error = anyhow::Error;
538
539    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
540        bytes.parse::<T>().map_err(anyhow::Error::from)
541    }
542}
543
544#[cfg(test)]
545pub(super) mod tests {
546    use super::security_context::SecurityContext;
547    use super::{
548        AccessVector, ClassId, HandleUnknown, Policy, TypeId, XpermsAccessDecision, XpermsBitmap,
549        XpermsKind, parse_policy_by_value,
550    };
551    use crate::new_policy::traits::HasPolicyId;
552    use crate::{FileClass, InitialSid, KernelClass};
553
554    use anyhow::Context as _;
555    use serde::Deserialize;
556    use std::ops::Deref;
557
558    /// Returns whether the input types are explicitly granted `permission` via an `allow [...];`
559    /// policy statement.
560    ///
561    /// # Panics
562    /// If supplied with type Ids not previously obtained from the `Policy` itself; validation
563    /// ensures that all such Ids have corresponding definitions.
564    /// If either of `target_class` or `permission` cannot be resolved in the policy.
565    fn is_explicitly_allowed(
566        policy: &Policy,
567        source_type: TypeId,
568        target_type: TypeId,
569        target_class: &str,
570        permission: &str,
571    ) -> bool {
572        let classes = policy.classes();
573        let class = classes.get_by_name(target_class.as_bytes()).expect("class not found");
574        let class_permissions = policy
575            .find_class_permissions_by_name(target_class)
576            .expect("class permissions not found");
577        let (permission_id, _) = class_permissions
578            .iter()
579            .find(|(_, name)| permission.as_bytes() == name)
580            .expect("permission not found");
581        let permission_bit = AccessVector::from(*permission_id);
582        let access_decision = policy.0.compute_explicitly_allowed(source_type, target_type, class);
583        permission_bit == access_decision.allow & permission_bit
584    }
585
586    #[derive(Debug, Deserialize)]
587    struct Expectations {
588        expected_policy_version: u32,
589        expected_handle_unknown: LocalHandleUnknown,
590    }
591
592    #[derive(Debug, Deserialize, PartialEq)]
593    #[serde(rename_all = "snake_case")]
594    enum LocalHandleUnknown {
595        Deny,
596        Reject,
597        Allow,
598    }
599
600    impl PartialEq<HandleUnknown> for LocalHandleUnknown {
601        fn eq(&self, other: &HandleUnknown) -> bool {
602            match self {
603                LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
604                LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
605                LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
606            }
607        }
608    }
609
610    /// Given a vector of integer (u8) values, returns a bitmap in which the set bits correspond to
611    /// the indices of the provided values.
612    fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
613        let mut bitmap = [0u64; 4];
614        for element in elements {
615            let block_index = (*element as usize) / 64;
616            let bit_index = (*element as usize) % 64;
617            bitmap[block_index] |= 1u64 << bit_index;
618        }
619        XpermsBitmap::new(bitmap)
620    }
621
622    #[test]
623    fn known_policies() {
624        let policies_and_expectations = [
625            [
626                b"testdata/policies/emulator".to_vec(),
627                include_bytes!("../../testdata/policies/emulator").to_vec(),
628                include_bytes!("../../testdata/expectations/emulator").to_vec(),
629            ],
630            [
631                b"testdata/policies/selinux_testsuite".to_vec(),
632                include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
633                include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
634            ],
635        ];
636
637        for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
638            let expectations = serde_json5::from_reader::<_, Expectations>(
639                &mut std::io::Cursor::new(expectations_bytes),
640            )
641            .expect("deserialize expectations");
642
643            // Test parse-by-value.
644
645            let unvalidated_policy =
646                parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
647
648            let policy = unvalidated_policy
649                .validate()
650                .with_context(|| {
651                    format!(
652                        "policy path: {:?}",
653                        std::str::from_utf8(policy_path.as_slice()).unwrap()
654                    )
655                })
656                .expect("validate policy");
657
658            assert_eq!(expectations.expected_policy_version, policy.policy_version());
659            assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
660
661            // Returned policy bytes must be identical to input policy bytes.
662            let binary_policy = policy.serialize();
663            assert_eq!(&policy_bytes, binary_policy.deref());
664        }
665    }
666
667    #[test]
668    fn policy_lookup() {
669        let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
670        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
671        let policy = policy.validate().expect("validate selinux testsuite policy");
672
673        let unconfined_t = policy.types().get_by_name(b"unconfined_t").expect("look up type").id();
674
675        assert!(is_explicitly_allowed(&policy, unconfined_t, unconfined_t, "process", "fork",));
676    }
677
678    #[test]
679    fn initial_contexts() {
680        let policy_bytes =
681            include_bytes!("../../testdata/micro_policies/multiple_levels_and_categories_policy");
682        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
683        let policy = policy.validate().expect("validate policy");
684
685        let kernel_context = policy.initial_context(InitialSid::Kernel);
686        assert_eq!(
687            policy.serialize_security_context(&kernel_context),
688            b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
689        )
690    }
691
692    #[test]
693    fn explicit_allow_type_type() {
694        let policy_bytes =
695            include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy");
696        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
697        let policy = policy.validate().expect("validate policy");
698
699        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
700        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
701
702        assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
703    }
704
705    #[test]
706    fn no_explicit_allow_type_type() {
707        let policy_bytes =
708            include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy");
709        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
710        let policy = policy.validate().expect("validate policy");
711
712        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
713        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
714
715        assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
716    }
717
718    #[test]
719    fn explicit_allow_type_attr() {
720        let policy_bytes =
721            include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy");
722        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
723        let policy = policy.validate().expect("validate policy");
724
725        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
726        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
727
728        assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
729    }
730
731    #[test]
732    fn no_explicit_allow_type_attr() {
733        let policy_bytes =
734            include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy");
735        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
736        let policy = policy.validate().expect("validate policy");
737
738        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
739        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
740
741        assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
742    }
743
744    #[test]
745    fn explicit_allow_attr_attr() {
746        let policy_bytes =
747            include_bytes!("../../testdata/micro_policies/allow_a_attr_b_attr_class0_perm0_policy");
748        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
749        let policy = policy.validate().expect("validate policy");
750
751        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
752        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
753
754        assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
755    }
756
757    #[test]
758    fn no_explicit_allow_attr_attr() {
759        let policy_bytes = include_bytes!(
760            "../../testdata/micro_policies/no_allow_a_attr_b_attr_class0_perm0_policy"
761        );
762        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
763        let policy = policy.validate().expect("validate policy");
764
765        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
766        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
767
768        assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
769    }
770
771    #[test]
772    fn compute_explicitly_allowed_multiple_attributes() {
773        let policy_bytes = include_bytes!(
774            "../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy"
775        );
776        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
777        let policy = policy.validate().expect("validate policy");
778
779        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
780
781        let classes = policy.classes();
782        let class = classes.get_by_name(b"class0").expect("class not found");
783        let raw_access_vector = policy.0.compute_explicitly_allowed(a_t, a_t, class).allow.value();
784
785        // Two separate attributes are each allowed one permission on `[attr] self:class0`. Both
786        // attributes are associated with "a_t". No other `allow` statements appear in the policy
787        // in relation to "a_t". Therefore, we expect exactly two 1's in the access vector for
788        // query `("a_t", "a_t", "class0")`.
789        assert_eq!(2, raw_access_vector.count_ones());
790    }
791
792    #[test]
793    fn compute_access_decision_with_constraints() {
794        let policy_bytes =
795            include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy");
796        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
797        let policy = policy.validate().expect("validate policy");
798
799        let source_context: SecurityContext = policy
800            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
801            .expect("create source security context");
802
803        let target_context_satisfied: SecurityContext = source_context.clone();
804        let decision_satisfied = policy.compute_access_decision(
805            &source_context,
806            &target_context_satisfied,
807            KernelClass::File,
808        );
809        // The class `file` has 4 permissions, 3 of which are explicitly
810        // allowed for this target context. All of those permissions satisfy all
811        // matching constraints.
812        assert_eq!(decision_satisfied.allow, AccessVector::from(7));
813
814        let target_context_unsatisfied: SecurityContext = policy
815            .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
816            .expect("create target security context failing some constraints");
817        let decision_unsatisfied = policy.compute_access_decision(
818            &source_context,
819            &target_context_unsatisfied,
820            KernelClass::File,
821        );
822        // Two of the explicitly-allowed permissions fail to satisfy a matching
823        // constraint. Only 1 is allowed in the final access decision.
824        assert_eq!(decision_unsatisfied.allow, AccessVector::from(4));
825    }
826
827    #[test]
828    fn compute_ioctl_access_decision_explicitly_allowed() {
829        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
830        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
831        let policy = policy.validate().expect("validate policy");
832
833        let source_context: SecurityContext = policy
834            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
835            .expect("create source security context");
836        let target_context_matched: SecurityContext = source_context.clone();
837
838        // `allowxperm` rules for the `file` class:
839        //
840        // `allowxperm type0 self:file ioctl { 0xabcd };`
841        // `allowxperm type0 self:file ioctl { 0xabef };`
842        // `allowxperm type0 self:file ioctl { 0x1000 - 0x10ff };`
843        //
844        // `auditallowxperm` rules for the `file` class:
845        //
846        // auditallowxperm type0 self:file ioctl { 0xabcd };
847        // auditallowxperm type0 self:file ioctl { 0xabef };
848        // auditallowxperm type0 self:file ioctl { 0x1000 - 0x10ff };
849        //
850        // `dontauditxperm` rules for the `file` class:
851        //
852        // dontauditxperm type0 self:file ioctl { 0xabcd };
853        // dontauditxperm type0 self:file ioctl { 0xabef };
854        // dontauditxperm type0 self:file ioctl { 0x1000 - 0x10ff };
855        let decision_single = policy.compute_xperms_access_decision(
856            XpermsKind::Ioctl,
857            &source_context,
858            &target_context_matched,
859            KernelClass::File,
860            0xab,
861        );
862
863        let mut expected_auditdeny =
864            xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
865        expected_auditdeny -= xperms_bitmap_from_elements(&[0xcd, 0xef]);
866
867        let expected_decision_single = XpermsAccessDecision {
868            allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
869            auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
870            auditdeny: expected_auditdeny,
871        };
872        assert_eq!(decision_single, expected_decision_single);
873
874        let decision_range = policy.compute_xperms_access_decision(
875            XpermsKind::Ioctl,
876            &source_context,
877            &target_context_matched,
878            KernelClass::File,
879            0x10,
880        );
881        let expected_decision_range = XpermsAccessDecision {
882            allow: XpermsBitmap::ALL,
883            auditallow: XpermsBitmap::ALL,
884            auditdeny: XpermsBitmap::NONE,
885        };
886        assert_eq!(decision_range, expected_decision_range);
887    }
888
889    #[test]
890    fn compute_ioctl_access_decision_denied() {
891        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
892        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
893        let class_id = unvalidated
894            .0
895            .classes()
896            .get_by_name(b"class_one_ioctl")
897            .expect("look up class_one_ioctl")
898            .id();
899        let policy = unvalidated.validate().expect("validate policy");
900        let source_context: SecurityContext = policy
901            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
902            .expect("create source security context");
903        let target_context_matched: SecurityContext = source_context.clone();
904
905        // `allowxperm` rules for the `class_one_ioctl` class:
906        //
907        // `allowxperm type0 self:class_one_ioctl ioctl { 0xabcd };`
908        let decision_single = policy.compute_xperms_access_decision(
909            XpermsKind::Ioctl,
910            &source_context,
911            &target_context_matched,
912            class_id,
913            0xdb,
914        );
915
916        let expected_decision = XpermsAccessDecision {
917            allow: XpermsBitmap::NONE,
918            auditallow: XpermsBitmap::NONE,
919            auditdeny: XpermsBitmap::ALL,
920        };
921        assert_eq!(decision_single, expected_decision);
922    }
923
924    #[test]
925    fn compute_ioctl_access_decision_unmatched() {
926        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
927        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
928        let policy = policy.validate().expect("validate policy");
929
930        let source_context: SecurityContext = policy
931            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
932            .expect("create source security context");
933
934        // No matching ioctl xperm-related statements for this target's type
935        let target_context_unmatched: SecurityContext = policy
936            .parse_security_context(b"user0:object_r:type1:s0-s0".into())
937            .expect("create source security context");
938
939        for prefix in 0x0..=0xff {
940            let decision = policy.compute_xperms_access_decision(
941                XpermsKind::Ioctl,
942                &source_context,
943                &target_context_unmatched,
944                KernelClass::File,
945                prefix,
946            );
947            assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
948        }
949    }
950
951    #[test]
952    fn compute_ioctl_earlier_redundant_prefixful_not_coalesced_into_prefixless() {
953        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
954        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
955        let class_id = unvalidated
956            .0
957            .classes()
958            .get_by_name(b"class_earlier_redundant_prefixful_not_coalesced_into_prefixless")
959            .expect("look up class_earlier_redundant_prefixful_not_coalesced_into_prefixless")
960            .id();
961        let policy = unvalidated.validate().expect("validate policy");
962        let source_context: SecurityContext = policy
963            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
964            .expect("create source security context");
965        let target_context_matched: SecurityContext = source_context.clone();
966
967        // `allowxperm` rules for the `class_earlier_redundant_prefixful_not_coalesced_into_prefixless` class:
968        //
969        // `allowxperm type0 self:class_earlier_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x8001-0x8002 };`
970        // `allowxperm type0 self:class_earlier_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x8000-0x80ff };`
971        let decision = policy.compute_xperms_access_decision(
972            XpermsKind::Ioctl,
973            &source_context,
974            &target_context_matched,
975            class_id,
976            0x7f,
977        );
978        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
979        let decision = policy.compute_xperms_access_decision(
980            XpermsKind::Ioctl,
981            &source_context,
982            &target_context_matched,
983            class_id,
984            0x80,
985        );
986        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
987        let decision = policy.compute_xperms_access_decision(
988            XpermsKind::Ioctl,
989            &source_context,
990            &target_context_matched,
991            class_id,
992            0x81,
993        );
994        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
995    }
996
997    #[test]
998    fn compute_ioctl_later_redundant_prefixful_not_coalesced_into_prefixless() {
999        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1000        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1001        let class_id = unvalidated
1002            .0
1003            .classes()
1004            .get_by_name(b"class_later_redundant_prefixful_not_coalesced_into_prefixless")
1005            .expect("look up class_later_redundant_prefixful_not_coalesced_into_prefixless")
1006            .id();
1007        let policy = unvalidated.validate().expect("validate policy");
1008        let source_context: SecurityContext = policy
1009            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1010            .expect("create source security context");
1011        let target_context_matched: SecurityContext = source_context.clone();
1012
1013        // `allowxperm` rules for the `class_later_redundant_prefixful_not_coalesced_into_prefixless` class:
1014        //
1015        // `allowxperm type0 self:class_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x9000-0x90ff };`
1016        // `allowxperm type0 self:class_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x90fd-0x90fe };`
1017        let decision = policy.compute_xperms_access_decision(
1018            XpermsKind::Ioctl,
1019            &source_context,
1020            &target_context_matched,
1021            class_id,
1022            0x8f,
1023        );
1024        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1025        let decision = policy.compute_xperms_access_decision(
1026            XpermsKind::Ioctl,
1027            &source_context,
1028            &target_context_matched,
1029            class_id,
1030            0x90,
1031        );
1032        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1033        let decision = policy.compute_xperms_access_decision(
1034            XpermsKind::Ioctl,
1035            &source_context,
1036            &target_context_matched,
1037            class_id,
1038            0x91,
1039        );
1040        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1041    }
1042
1043    #[test]
1044    fn compute_ioctl_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless() {
1045        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1046        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1047        let class_id = unvalidated
1048            .0
1049            .classes()
1050            .get_by_name(
1051                b"class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless",
1052            )
1053            .expect(
1054                "look up class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless",
1055            )
1056            .id();
1057        let policy = unvalidated.validate().expect("validate policy");
1058        let source_context: SecurityContext = policy
1059            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1060            .expect("create source security context");
1061        let target_context_matched: SecurityContext = source_context.clone();
1062
1063        // `allowxperm` rules for the `class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless` class:
1064        //
1065        // `allowxperm type0 self:class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0xa001-0xa002 };`
1066        // `allowxperm type0 self:class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0xa000-0xa03f 0xa040-0xa0ff };`
1067        // `allowxperm type0 self:class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0xa0fd-0xa0fe };`
1068        let decision = policy.compute_xperms_access_decision(
1069            XpermsKind::Ioctl,
1070            &source_context,
1071            &target_context_matched,
1072            class_id,
1073            0x9f,
1074        );
1075        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1076        let decision = policy.compute_xperms_access_decision(
1077            XpermsKind::Ioctl,
1078            &source_context,
1079            &target_context_matched,
1080            class_id,
1081            0xa0,
1082        );
1083        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1084        let decision = policy.compute_xperms_access_decision(
1085            XpermsKind::Ioctl,
1086            &source_context,
1087            &target_context_matched,
1088            class_id,
1089            0xa1,
1090        );
1091        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1092    }
1093
1094    #[test]
1095    fn compute_ioctl_prefixfuls_that_coalesce_to_prefixless() {
1096        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1097        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1098        let class_id: ClassId = unvalidated
1099            .0
1100            .classes()
1101            .get_by_name(b"class_prefixfuls_that_coalesce_to_prefixless")
1102            .expect("look up class_prefixfuls_that_coalesce_to_prefixless")
1103            .id();
1104        let policy = unvalidated.validate().expect("validate policy");
1105        let source_context: SecurityContext = policy
1106            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1107            .expect("create source security context");
1108        let target_context_matched: SecurityContext = source_context.clone();
1109
1110        // `allowxperm` rules for the `class_prefixfuls_that_coalesce_to_prefixless` class:
1111        //
1112        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless ioctl { 0xb000 0xb001 0xb002 };`
1113        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless ioctl { 0xb003-0xb0fc };`
1114        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless ioctl { 0xb0fd 0xb0fe 0xb0ff };`
1115        let decision = policy.compute_xperms_access_decision(
1116            XpermsKind::Ioctl,
1117            &source_context,
1118            &target_context_matched,
1119            class_id,
1120            0xaf,
1121        );
1122        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1123        let decision = policy.compute_xperms_access_decision(
1124            XpermsKind::Ioctl,
1125            &source_context,
1126            &target_context_matched,
1127            class_id,
1128            0xb0,
1129        );
1130        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1131        let decision = policy.compute_xperms_access_decision(
1132            XpermsKind::Ioctl,
1133            &source_context,
1134            &target_context_matched,
1135            class_id,
1136            0xb1,
1137        );
1138        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1139    }
1140
1141    #[test]
1142    fn compute_ioctl_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless() {
1143        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1144        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1145        let class_id = unvalidated
1146            .0
1147            .classes()
1148            .get_by_name(b"class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless")
1149            .expect("look up class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless")
1150            .id();
1151        let policy = unvalidated.validate().expect("validate policy");
1152        let source_context: SecurityContext = policy
1153            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1154            .expect("create source security context");
1155        let target_context_matched: SecurityContext = source_context.clone();
1156
1157        // `allowxperm` rules for the `class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless` class:
1158        //
1159        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc000 0xc001 0xc002 0xc003 };`
1160        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc004-0xc0fb };`
1161        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc0fc 0xc0fd 0xc0fe 0xc0ff };`
1162        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc100-0xc1ff };`
1163        let decision = policy.compute_xperms_access_decision(
1164            XpermsKind::Ioctl,
1165            &source_context,
1166            &target_context_matched,
1167            class_id,
1168            0xbf,
1169        );
1170        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1171        let decision = policy.compute_xperms_access_decision(
1172            XpermsKind::Ioctl,
1173            &source_context,
1174            &target_context_matched,
1175            class_id,
1176            0xc0,
1177        );
1178        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1179        let decision = policy.compute_xperms_access_decision(
1180            XpermsKind::Ioctl,
1181            &source_context,
1182            &target_context_matched,
1183            class_id,
1184            0xc1,
1185        );
1186        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1187        let decision = policy.compute_xperms_access_decision(
1188            XpermsKind::Ioctl,
1189            &source_context,
1190            &target_context_matched,
1191            class_id,
1192            0xc2,
1193        );
1194        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1195    }
1196
1197    #[test]
1198    fn compute_ioctl_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless() {
1199        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1200        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1201        let class_id = unvalidated
1202            .0
1203            .classes()
1204            .get_by_name(b"class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless")
1205            .expect("look up class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless")
1206            .id();
1207        let policy = unvalidated.validate().expect("validate policy");
1208        let source_context: SecurityContext = policy
1209            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1210            .expect("create source security context");
1211        let target_context_matched: SecurityContext = source_context.clone();
1212
1213        // `allowxperm` rules for the `class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless` class:
1214        //
1215        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd600-0xd6ff };`
1216        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd700 0xd701 0xd702 0xd703 };`
1217        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd704-0xd7fb };`
1218        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd7fc 0xd7fd 0xd7fe 0xd7ff };`
1219        let decision = policy.compute_xperms_access_decision(
1220            XpermsKind::Ioctl,
1221            &source_context,
1222            &target_context_matched,
1223            class_id,
1224            0xd5,
1225        );
1226        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1227        let decision = policy.compute_xperms_access_decision(
1228            XpermsKind::Ioctl,
1229            &source_context,
1230            &target_context_matched,
1231            class_id,
1232            0xd6,
1233        );
1234        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1235        let decision = policy.compute_xperms_access_decision(
1236            XpermsKind::Ioctl,
1237            &source_context,
1238            &target_context_matched,
1239            class_id,
1240            0xd7,
1241        );
1242        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1243        let decision = policy.compute_xperms_access_decision(
1244            XpermsKind::Ioctl,
1245            &source_context,
1246            &target_context_matched,
1247            class_id,
1248            0xd8,
1249        );
1250        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1251    }
1252
1253    // As of 2025-12, the policy compiler generates allow rules in an unexpected order in the
1254    // policy binary for this oddly-expressed policy text content (with one "prefixful" rule
1255    // of type [`XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES`], then the "prefixless" rule of type
1256    // `XPERMS_TYPE_IOCTL_PREFIXES`, and then two more rules of type
1257    // `XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES`). These rules are still contiguous and without
1258    // interruption by rules of other source-target-class-type quadruplets; it's just unexpected
1259    // that the "prefixless" one falls in the middle of the "prefixful" ones rather than
1260    // consistently at the beginning or the end of the "prefixful" ones. We don't directly test
1261    // that our odd text content leads to this curious binary content, but we do test that we
1262    // make correct access decisions.
1263    #[test]
1264    fn compute_ioctl_ridiculous_permission_ordering() {
1265        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1266        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1267        let class_id = unvalidated
1268            .0
1269            .classes()
1270            .get_by_name(b"class_ridiculous_permission_ordering")
1271            .expect("look up class_ridiculous_permission_ordering")
1272            .id();
1273        let policy = unvalidated.validate().expect("validate policy");
1274        let source_context: SecurityContext = policy
1275            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1276            .expect("create source security context");
1277        let target_context_matched: SecurityContext = source_context.clone();
1278
1279        // `allowxperm` rules for the `class_ridiculous_permission_ordering` class:
1280        //
1281        // `allowxperm type0 self:class_ridiculous_permission_ordering ioctl { 0xfdfa-0xfdfd 0xf001 };`
1282        // `allowxperm type0 self:class_ridiculous_permission_ordering ioctl { 0x0080-0x00ff 0xfdfa-0xfdfd 0x0011-0x0017 0x0001 0x0001 0x0001 0xc000-0xcff2 0x0000 0x0011-0x0017 0x0001 0x0005-0x0015 0x0002-0x007f };`
1283        let decision = policy.compute_xperms_access_decision(
1284            XpermsKind::Ioctl,
1285            &source_context,
1286            &target_context_matched,
1287            class_id,
1288            0x00,
1289        );
1290        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1291        let decision = policy.compute_xperms_access_decision(
1292            XpermsKind::Ioctl,
1293            &source_context,
1294            &target_context_matched,
1295            class_id,
1296            0x01,
1297        );
1298        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1299        let decision = policy.compute_xperms_access_decision(
1300            XpermsKind::Ioctl,
1301            &source_context,
1302            &target_context_matched,
1303            class_id,
1304            0xbf,
1305        );
1306        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1307        let decision = policy.compute_xperms_access_decision(
1308            XpermsKind::Ioctl,
1309            &source_context,
1310            &target_context_matched,
1311            class_id,
1312            0xc0,
1313        );
1314        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1315        let decision = policy.compute_xperms_access_decision(
1316            XpermsKind::Ioctl,
1317            &source_context,
1318            &target_context_matched,
1319            class_id,
1320            0xce,
1321        );
1322        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1323        let decision = policy.compute_xperms_access_decision(
1324            XpermsKind::Ioctl,
1325            &source_context,
1326            &target_context_matched,
1327            class_id,
1328            0xcf,
1329        );
1330        assert_eq!(
1331            decision,
1332            XpermsAccessDecision {
1333                allow: xperms_bitmap_from_elements((0x0..=0xf2).collect::<Vec<_>>().as_slice()),
1334                auditallow: XpermsBitmap::NONE,
1335                auditdeny: XpermsBitmap::ALL,
1336            }
1337        );
1338        let decision = policy.compute_xperms_access_decision(
1339            XpermsKind::Ioctl,
1340            &source_context,
1341            &target_context_matched,
1342            class_id,
1343            0xd0,
1344        );
1345        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1346        let decision = policy.compute_xperms_access_decision(
1347            XpermsKind::Ioctl,
1348            &source_context,
1349            &target_context_matched,
1350            class_id,
1351            0xe9,
1352        );
1353        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1354        let decision = policy.compute_xperms_access_decision(
1355            XpermsKind::Ioctl,
1356            &source_context,
1357            &target_context_matched,
1358            class_id,
1359            0xf0,
1360        );
1361        assert_eq!(
1362            decision,
1363            XpermsAccessDecision {
1364                allow: xperms_bitmap_from_elements(&[0x01]),
1365                auditallow: XpermsBitmap::NONE,
1366                auditdeny: XpermsBitmap::ALL,
1367            }
1368        );
1369        let decision = policy.compute_xperms_access_decision(
1370            XpermsKind::Ioctl,
1371            &source_context,
1372            &target_context_matched,
1373            class_id,
1374            0xf1,
1375        );
1376        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1377        let decision = policy.compute_xperms_access_decision(
1378            XpermsKind::Ioctl,
1379            &source_context,
1380            &target_context_matched,
1381            class_id,
1382            0xfc,
1383        );
1384        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1385        let decision = policy.compute_xperms_access_decision(
1386            XpermsKind::Ioctl,
1387            &source_context,
1388            &target_context_matched,
1389            class_id,
1390            0xfd,
1391        );
1392        assert_eq!(
1393            decision,
1394            XpermsAccessDecision {
1395                allow: xperms_bitmap_from_elements((0xfa..=0xfd).collect::<Vec<_>>().as_slice()),
1396                auditallow: XpermsBitmap::NONE,
1397                auditdeny: XpermsBitmap::ALL,
1398            }
1399        );
1400        let decision = policy.compute_xperms_access_decision(
1401            XpermsKind::Ioctl,
1402            &source_context,
1403            &target_context_matched,
1404            class_id,
1405            0xfe,
1406        );
1407        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1408    }
1409
1410    #[test]
1411    fn compute_nlmsg_access_decision_explicitly_allowed() {
1412        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1413        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1414        let policy = policy.validate().expect("validate policy");
1415
1416        let source_context: SecurityContext = policy
1417            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1418            .expect("create source security context");
1419        let target_context_matched: SecurityContext = source_context.clone();
1420
1421        // `allowxperm` rules for the `netlink_route_socket` class:
1422        //
1423        // `allowxperm type0 self:netlink_route_socket nlmsg { 0xabcd };`
1424        // `allowxperm type0 self:netlink_route_socket nlmsg { 0xabef };`
1425        // `allowxperm type0 self:netlink_route_socket nlmsg { 0x1000 - 0x10ff };`
1426        //
1427        // `auditallowxperm` rules for the `netlink_route_socket` class:
1428        //
1429        // auditallowxperm type0 self:netlink_route_socket nlmsg { 0xabcd };
1430        // auditallowxperm type0 self:netlink_route_socket nlmsg { 0xabef };
1431        // auditallowxperm type0 self:netlink_route_socket nlmsg { 0x1000 - 0x10ff };
1432        //
1433        // `dontauditxperm` rules for the `netlink_route_socket` class:
1434        //
1435        // dontauditxperm type0 self:netlink_route_socket nlmsg { 0xabcd };
1436        // dontauditxperm type0 self:netlink_route_socket nlmsg { 0xabef };
1437        // dontauditxperm type0 self:netlink_route_socket nlmsg { 0x1000 - 0x10ff };
1438        let decision_single = policy.compute_xperms_access_decision(
1439            XpermsKind::Nlmsg,
1440            &source_context,
1441            &target_context_matched,
1442            KernelClass::NetlinkRouteSocket,
1443            0xab,
1444        );
1445
1446        let mut expected_auditdeny =
1447            xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1448        expected_auditdeny -= xperms_bitmap_from_elements(&[0xcd, 0xef]);
1449
1450        let expected_decision_single = XpermsAccessDecision {
1451            allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1452            auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1453            auditdeny: expected_auditdeny,
1454        };
1455        assert_eq!(decision_single, expected_decision_single);
1456
1457        let decision_range = policy.compute_xperms_access_decision(
1458            XpermsKind::Nlmsg,
1459            &source_context,
1460            &target_context_matched,
1461            KernelClass::NetlinkRouteSocket,
1462            0x10,
1463        );
1464        let expected_decision_range = XpermsAccessDecision {
1465            allow: XpermsBitmap::ALL,
1466            auditallow: XpermsBitmap::ALL,
1467            auditdeny: XpermsBitmap::NONE,
1468        };
1469        assert_eq!(decision_range, expected_decision_range);
1470    }
1471
1472    #[test]
1473    fn compute_nlmsg_access_decision_unmatched() {
1474        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1475        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1476        let policy = policy.validate().expect("validate policy");
1477
1478        let source_context: SecurityContext = policy
1479            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1480            .expect("create source security context");
1481
1482        // No matching nlmsg xperm-related statements for this target's type
1483        let target_context_unmatched: SecurityContext = policy
1484            .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1485            .expect("create source security context");
1486
1487        for prefix in 0x0..=0xff {
1488            let decision = policy.compute_xperms_access_decision(
1489                XpermsKind::Nlmsg,
1490                &source_context,
1491                &target_context_unmatched,
1492                KernelClass::NetlinkRouteSocket,
1493                prefix,
1494            );
1495            assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1496        }
1497    }
1498
1499    #[test]
1500    fn compute_ioctl_grant_does_not_cause_nlmsg_deny() {
1501        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1502        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1503        let class_id = unvalidated
1504            .0
1505            .classes()
1506            .get_by_name(b"class_ioctl_grant_does_not_cause_nlmsg_deny")
1507            .expect("look up class_ioctl_grant_does_not_cause_nlmsg_deny")
1508            .id();
1509        let policy = unvalidated.validate().expect("validate policy");
1510        let source_context: SecurityContext = policy
1511            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1512            .expect("create source security context");
1513        let target_context_matched: SecurityContext = source_context.clone();
1514
1515        // `allowxperm` rules for the `class_ioctl_grant_does_not_cause_nlmsg_deny` class:
1516        //
1517        // `allowxperm type0 self:class_ioctl_grant_does_not_cause_nlmsg_deny ioctl { 0x0002 };`
1518        let ioctl_decision = policy.compute_xperms_access_decision(
1519            XpermsKind::Ioctl,
1520            &source_context,
1521            &target_context_matched,
1522            class_id,
1523            0x00,
1524        );
1525        assert_eq!(
1526            ioctl_decision,
1527            XpermsAccessDecision {
1528                allow: xperms_bitmap_from_elements(&[0x0002]),
1529                auditallow: XpermsBitmap::NONE,
1530                auditdeny: XpermsBitmap::ALL,
1531            }
1532        );
1533        let nlmsg_decision = policy.compute_xperms_access_decision(
1534            XpermsKind::Nlmsg,
1535            &source_context,
1536            &target_context_matched,
1537            class_id,
1538            0x00,
1539        );
1540        assert_eq!(nlmsg_decision, XpermsAccessDecision::ALLOW_ALL);
1541    }
1542
1543    #[test]
1544    fn compute_nlmsg_grant_does_not_cause_ioctl_deny() {
1545        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1546        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1547        let class_id = unvalidated
1548            .0
1549            .classes()
1550            .get_by_name(b"class_nlmsg_grant_does_not_cause_ioctl_deny")
1551            .expect("look up class_nlmsg_grant_does_not_cause_ioctl_deny")
1552            .id();
1553        let policy = unvalidated.validate().expect("validate policy");
1554        let source_context: SecurityContext = policy
1555            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1556            .expect("create source security context");
1557        let target_context_matched: SecurityContext = source_context.clone();
1558
1559        // `allowxperm` rules for the `class_nlmsg_grant_does_not_cause_ioctl_deny` class:
1560        //
1561        // `allowxperm type0 self:class_nlmsg_grant_does_not_cause_ioctl_deny nlmsg { 0x0003 };`
1562        let nlmsg_decision = policy.compute_xperms_access_decision(
1563            XpermsKind::Nlmsg,
1564            &source_context,
1565            &target_context_matched,
1566            class_id,
1567            0x00,
1568        );
1569        assert_eq!(
1570            nlmsg_decision,
1571            XpermsAccessDecision {
1572                allow: xperms_bitmap_from_elements(&[0x0003]),
1573                auditallow: XpermsBitmap::NONE,
1574                auditdeny: XpermsBitmap::ALL,
1575            }
1576        );
1577        let ioctl_decision = policy.compute_xperms_access_decision(
1578            XpermsKind::Ioctl,
1579            &source_context,
1580            &target_context_matched,
1581            class_id,
1582            0x00,
1583        );
1584        assert_eq!(ioctl_decision, XpermsAccessDecision::ALLOW_ALL);
1585    }
1586
1587    #[test]
1588    fn compute_create_context_minimal() {
1589        let policy_bytes =
1590            include_bytes!("../../testdata/composite_policies/compiled/minimal_policy");
1591        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1592        let policy = policy.validate().expect("validate policy");
1593        let source = policy
1594            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1595            .expect("valid source security context");
1596        let target = policy
1597            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1598            .expect("valid target security context");
1599
1600        let actual = policy.compute_create_context(&source, &target, FileClass::File);
1601        let expected: SecurityContext = policy
1602            .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1603            .expect("valid expected security context");
1604
1605        assert_eq!(expected, actual);
1606    }
1607
1608    #[test]
1609    fn new_security_context_minimal() {
1610        let policy_bytes =
1611            include_bytes!("../../testdata/composite_policies/compiled/minimal_policy");
1612        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1613        let policy = policy.validate().expect("validate policy");
1614        let source = policy
1615            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1616            .expect("valid source security context");
1617        let target = policy
1618            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1619            .expect("valid target security context");
1620
1621        let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1622
1623        assert_eq!(source, actual);
1624    }
1625
1626    #[test]
1627    fn compute_create_context_class_defaults() {
1628        let policy_bytes =
1629            include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy");
1630        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1631        let policy = policy.validate().expect("validate policy");
1632        let source = policy
1633            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1634            .expect("valid source security context");
1635        let target = policy
1636            .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1637            .expect("valid target security context");
1638
1639        let actual = policy.compute_create_context(&source, &target, FileClass::File);
1640        let expected: SecurityContext = policy
1641            .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1642            .expect("valid expected security context");
1643
1644        assert_eq!(expected, actual);
1645    }
1646
1647    #[test]
1648    fn new_security_context_class_defaults() {
1649        let policy_bytes =
1650            include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy");
1651        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1652        let policy = policy.validate().expect("validate policy");
1653        let source = policy
1654            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1655            .expect("valid source security context");
1656        let target = policy
1657            .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1658            .expect("valid target security context");
1659
1660        let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1661        let expected: SecurityContext = policy
1662            .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1663            .expect("valid expected security context");
1664
1665        assert_eq!(expected, actual);
1666    }
1667
1668    #[test]
1669    fn compute_create_context_role_transition() {
1670        let policy_bytes =
1671            include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy");
1672        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1673        let policy = policy.validate().expect("validate policy");
1674        let source = policy
1675            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1676            .expect("valid source security context");
1677        let target = policy
1678            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1679            .expect("valid target security context");
1680
1681        let actual = policy.compute_create_context(&source, &target, FileClass::File);
1682        let expected: SecurityContext = policy
1683            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1684            .expect("valid expected security context");
1685
1686        assert_eq!(expected, actual);
1687    }
1688
1689    #[test]
1690    fn new_security_context_role_transition() {
1691        let policy_bytes =
1692            include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy");
1693        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1694        let policy = policy.validate().expect("validate policy");
1695        let source = policy
1696            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1697            .expect("valid source security context");
1698        let target = policy
1699            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1700            .expect("valid target security context");
1701
1702        let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1703        let expected: SecurityContext = policy
1704            .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
1705            .expect("valid expected security context");
1706
1707        assert_eq!(expected, actual);
1708    }
1709
1710    #[test]
1711    fn compute_create_context_role_transition_not_allowed() {
1712        let policy_bytes = include_bytes!(
1713            "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy"
1714        );
1715        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1716        let policy = policy.validate().expect("validate policy");
1717        let source = policy
1718            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1719            .expect("valid source security context");
1720        let target = policy
1721            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1722            .expect("valid target security context");
1723
1724        let actual = policy.compute_create_context(&source, &target, FileClass::File);
1725        let expected: SecurityContext = policy
1726            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1727            .expect("valid expected security context");
1728
1729        // Role-allow rules are not checked during `compute_create_context()`; they are checked
1730        // during `compute_access_decision()` on the "process" class.
1731        assert_eq!(expected, actual);
1732        assert!(policy.validate_security_context(&actual).is_ok());
1733    }
1734
1735    #[test]
1736    fn compute_access_decision_role_allow() {
1737        // 1. With `role_transition_policy`, `allow source_r transition_r;` is present.
1738        let policy_bytes =
1739            include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy");
1740        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1741        let policy = policy.validate().expect("validate policy");
1742        let source = policy
1743            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1744            .expect("valid source security context");
1745        let target = policy
1746            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1747            .expect("valid target security context");
1748
1749        let decision_allowed =
1750            policy.compute_access_decision(&source, &target, KernelClass::Process);
1751        assert_ne!(decision_allowed.allow, AccessVector::NONE);
1752
1753        // 2. With `role_transition_not_allowed_policy`, `allow source_r transition_r;` is missing.
1754        let policy_bytes = include_bytes!(
1755            "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy"
1756        );
1757        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1758        let policy = policy.validate().expect("validate policy");
1759        let source = policy
1760            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1761            .expect("valid source security context");
1762        let target = policy
1763            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1764            .expect("valid target security context");
1765
1766        let decision_denied =
1767            policy.compute_access_decision(&source, &target, KernelClass::Process);
1768        assert_eq!(decision_denied.allow, AccessVector::NONE);
1769    }
1770
1771    #[test]
1772    fn compute_create_context_type_transition() {
1773        let policy_bytes =
1774            include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy");
1775        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1776        let policy = policy.validate().expect("validate policy");
1777        let source = policy
1778            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1779            .expect("valid source security context");
1780        let target = policy
1781            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1782            .expect("valid target security context");
1783
1784        let actual = policy.compute_create_context(&source, &target, FileClass::File);
1785        let expected: SecurityContext = policy
1786            .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
1787            .expect("valid expected security context");
1788
1789        assert_eq!(expected, actual);
1790    }
1791
1792    #[test]
1793    fn new_security_context_type_transition() {
1794        let policy_bytes =
1795            include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy");
1796        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1797        let policy = policy.validate().expect("validate policy");
1798        let source = policy
1799            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1800            .expect("valid source security context");
1801        let target = policy
1802            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1803            .expect("valid target security context");
1804
1805        let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1806        let expected: SecurityContext = policy
1807            .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
1808            .expect("valid expected security context");
1809
1810        assert_eq!(expected, actual);
1811    }
1812
1813    #[test]
1814    fn compute_create_context_range_transition() {
1815        let policy_bytes =
1816            include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy");
1817        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1818        let policy = policy.validate().expect("validate policy");
1819        let source = policy
1820            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1821            .expect("valid source security context");
1822        let target = policy
1823            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1824            .expect("valid target security context");
1825
1826        let actual = policy.compute_create_context(&source, &target, FileClass::File);
1827        let expected: SecurityContext = policy
1828            .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
1829            .expect("valid expected security context");
1830
1831        assert_eq!(expected, actual);
1832    }
1833
1834    #[test]
1835    fn new_security_context_range_transition() {
1836        let policy_bytes =
1837            include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy");
1838        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1839        let policy = policy.validate().expect("validate policy");
1840        let source = policy
1841            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1842            .expect("valid source security context");
1843        let target = policy
1844            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1845            .expect("valid target security context");
1846
1847        let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1848        let expected: SecurityContext = policy
1849            .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
1850            .expect("valid expected security context");
1851
1852        assert_eq!(expected, actual);
1853    }
1854
1855    #[test]
1856    fn access_vector_formats() {
1857        assert_eq!(format!("{:x}", AccessVector::NONE), "0");
1858        assert_eq!(format!("{:x}", AccessVector::ALL), "ffffffff");
1859        assert_eq!(format!("{:?}", AccessVector::NONE), "AccessVector(00000000)");
1860        assert_eq!(format!("{:?}", AccessVector::ALL), "AccessVector(ffffffff)");
1861    }
1862
1863    #[test]
1864    fn policy_genfscon_root_path() {
1865        let policy_bytes =
1866            include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy");
1867        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1868        let policy = policy.validate().expect("validate selinux policy");
1869
1870        {
1871            let context = policy.genfscon_label_for_fs_and_path(
1872                "fs_with_path_rules".into(),
1873                "/".into(),
1874                None,
1875            );
1876            assert_eq!(
1877                policy.serialize_security_context(&context.unwrap()),
1878                b"system_u:object_r:fs_with_path_rules_t:s0"
1879            )
1880        }
1881        {
1882            let context = policy.genfscon_label_for_fs_and_path(
1883                "fs_2_with_path_rules".into(),
1884                "/".into(),
1885                None,
1886            );
1887            assert_eq!(
1888                policy.serialize_security_context(&context.unwrap()),
1889                b"system_u:object_r:fs_2_with_path_rules_t:s0"
1890            )
1891        }
1892    }
1893
1894    #[test]
1895    fn policy_genfscon_subpaths() {
1896        let policy_bytes =
1897            include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy");
1898        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1899        let policy = policy.validate().expect("validate selinux policy");
1900
1901        let path_label_expectations = [
1902            // Matching paths defined in the policy:
1903            //    /a1/    -> fs_with_path_rules_a1_t
1904            //    /a1/b/c -> fs_with_path_rules_a1_b_c_t
1905            ("/a1/", "system_u:object_r:fs_with_path_rules_a1_t:s0"),
1906            ("/a1/b", "system_u:object_r:fs_with_path_rules_a1_t:s0"),
1907            ("/a1/b/c", "system_u:object_r:fs_with_path_rules_a1_b_c_t:s0"),
1908            // Matching paths defined in the policy:
1909            //    /a2/b    -> fs_with_path_rules_a2_b_t
1910            ("/a2/", "system_u:object_r:fs_with_path_rules_t:s0"),
1911            ("/a2/b/c/d", "system_u:object_r:fs_with_path_rules_a2_b_t:s0"),
1912            // Matching paths defined in the policy:
1913            //    /a3    -> fs_with_path_rules_a3_t
1914            ("/a3/b/c/d", "system_u:object_r:fs_with_path_rules_a3_t:s0"),
1915        ];
1916        for (path, expected_label) in path_label_expectations {
1917            let context = policy.genfscon_label_for_fs_and_path(
1918                "fs_with_path_rules".into(),
1919                path.into(),
1920                None,
1921            );
1922            assert_eq!(
1923                policy.serialize_security_context(&context.unwrap()),
1924                expected_label.as_bytes()
1925            )
1926        }
1927    }
1928
1929    #[test]
1930    fn policy_genfscon_mixed_order() {
1931        let policy_bytes =
1932            include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy");
1933        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1934        let policy = policy.validate().expect("validate selinux policy");
1935
1936        let path_label_expectations = [
1937            ("/", "system_u:object_r:fs_mixed_order_t:s0"),
1938            ("/a", "system_u:object_r:fs_mixed_order_a_t:s0"),
1939            ("/a/a", "system_u:object_r:fs_mixed_order_a_a_t:s0"),
1940            ("/a/b", "system_u:object_r:fs_mixed_order_a_b_t:s0"),
1941            ("/a/b/c", "system_u:object_r:fs_mixed_order_a_b_t:s0"),
1942        ];
1943        for (path, expected_label) in path_label_expectations {
1944            let context =
1945                policy.genfscon_label_for_fs_and_path("fs_mixed_order".into(), path.into(), None);
1946            assert_eq!(
1947                policy.serialize_security_context(&context.unwrap()),
1948                expected_label.as_bytes()
1949            );
1950        }
1951    }
1952}