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