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