Skip to main content

selinux/policy/
mod.rs

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