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