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