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