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