Skip to main content

selinux/policy/
mod.rs

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