Skip to main content

selinux/policy/
mod.rs

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