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