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