selinux/policy/
mod.rs

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