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 error;
6pub mod index;
7pub mod parsed_policy;
8pub mod parser;
9
10mod constraints;
11mod security_context;
12
13pub use crate::new_policy::traits::{HasName, HasPolicyId, PolicyId};
14pub use crate::new_policy::{
15    AccessDecision, AccessVector, AccessVectorRules, CategoryId, ClassId, FsUseType, HandleUnknown,
16    IndexedAccessVectorRules, MlsLevel, MlsRange, POLICYDB_VERSION_MAX, PermissionId, RoleId,
17    SELINUX_AVD_FLAGS_PERMISSIVE, SensitivityId, TypeId, User, UserId, XpermsBitmap,
18};
19use crate::{ClassPermission, KernelClass, NullessByteStr, ObjectClass, new_policy as new};
20pub use index::FsUseLabelAndType;
21use index::PolicyIndex;
22use parsed_policy::ParsedPolicy;
23pub use parser::PolicyCursor;
24use parser::PolicyData;
25pub use security_context::{SecurityContext, SecurityContextError};
26
27use anyhow::Context as _;
28use std::fmt::Debug;
29use std::num::NonZeroU32;
30use std::ops::Deref;
31
32use std::sync::Arc;
33use zerocopy::little_endian as le;
34
35/// Encapsulates the result of a permissions calculation, between
36/// source & target domains, for a specific class. Decisions describe
37
38/// A kind of extended permission, corresponding to the base permission that should trigger a check
39/// of an extended permission.
40#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
41pub enum XpermsKind {
42    Ioctl,
43    Nlmsg,
44}
45
46/// Encapsulates the result of an extended permissions calculation, between source & target
47/// domains, for a specific class, a specific kind of extended permissions, and for a specific
48/// xperm prefix byte. Decisions describe which 16-bit xperms are allowed, and whether xperms
49/// should be audit-logged when allowed, and when denied.
50#[derive(Debug, Clone, PartialEq)]
51pub struct XpermsAccessDecision {
52    pub allow: XpermsBitmap,
53    pub auditallow: XpermsBitmap,
54    pub auditdeny: XpermsBitmap,
55}
56
57impl XpermsAccessDecision {
58    pub const DENY_ALL: Self = Self {
59        allow: XpermsBitmap::NONE,
60        auditallow: XpermsBitmap::NONE,
61        auditdeny: XpermsBitmap::ALL,
62    };
63    pub const ALLOW_ALL: Self = Self {
64        allow: XpermsBitmap::ALL,
65        auditallow: XpermsBitmap::NONE,
66        auditdeny: XpermsBitmap::ALL,
67    };
68}
69
70/// Parses `binary_policy` by value; that is, copies underlying binary data out in addition to
71/// building up parser output structures. This function returns
72/// `(unvalidated_parser_output, binary_policy)` on success, or an error if parsing failed. Note
73/// that the second component of the success case contains precisely the same bytes as the input.
74/// This function depends on a uniformity of interface between the "by value" and "by reference"
75/// strategies, but also requires an `unvalidated_parser_output` type that is independent of the
76/// `binary_policy` lifetime. Taken together, these requirements demand the "move-in + move-out"
77/// interface for `binary_policy`.
78pub fn parse_policy_by_value(binary_policy: Vec<u8>) -> Result<Unvalidated, anyhow::Error> {
79    let policy_data: PolicyData = Arc::from(binary_policy);
80    let policy = ParsedPolicy::parse(policy_data).context("parsing policy")?;
81    Ok(Unvalidated(policy))
82}
83
84#[derive(Debug)]
85pub struct Policy(PolicyIndex);
86
87impl Deref for Policy {
88    type Target = PolicyIndex;
89
90    fn deref(&self) -> &Self::Target {
91        &self.0
92    }
93}
94
95impl Policy {
96    /// Serializes the policy back into [`PolicyData`].
97    pub fn serialize(&self) -> PolicyData {
98        let mut bytes = Vec::new();
99        self.0.serialize(&mut bytes).expect("serialization of new_policy should succeed");
100        std::sync::Arc::from(bytes)
101    }
102
103    pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
104        self.0
105            .conditional_booleans()
106            .iter()
107            .map(|boolean| (boolean.name(), boolean.active()))
108            .collect()
109    }
110
111    /// Returns the set of permissions for the given class, including both the
112    /// explicitly owned permissions and the inherited ones from common symbols.
113    /// Each permission is a tuple of the permission identifier (in the scope of
114    /// the given class) and the permission name.
115    pub fn find_class_permissions_by_name(
116        &self,
117        class_name: &str,
118    ) -> Result<Vec<(PermissionId, Vec<u8>)>, ()> {
119        let classes = self.classes();
120        let class = classes.get_by_name(class_name.as_bytes()).ok_or(())?;
121        let owned_permissions = class.permissions();
122
123        let mut result: Vec<_> = owned_permissions
124            .iter()
125            .map(|permission| (permission.id(), permission.name().to_vec()))
126            .collect();
127
128        // common_name() is empty when the class doesn't inherit from a CommonSymbol.
129        if class.common_name().is_empty() {
130            return Ok(result);
131        }
132
133        let common_symbol_permissions =
134            self.common_symbols().get_by_name(class.common_name()).ok_or(())?.permissions();
135
136        result.append(
137            &mut common_symbol_permissions
138                .iter()
139                .map(|permission| (permission.id(), permission.name().to_vec()))
140                .collect(),
141        );
142
143        Ok(result)
144    }
145
146    /// If there is an fs_use statement for the given filesystem type, returns the associated
147    /// [`SecurityContext`] and [`FsUseType`].
148    pub fn fs_use_label_and_type(&self, fs_type: NullessByteStr<'_>) -> Option<FsUseLabelAndType> {
149        self.0.fs_use_label_and_type(fs_type)
150    }
151
152    /// If there is a genfscon statement for the given filesystem type, returns the associated
153    /// [`SecurityContext`].
154    pub fn genfscon_label_for_fs_and_path(
155        &self,
156        fs_type: NullessByteStr<'_>,
157        node_path: NullessByteStr<'_>,
158        class_id: Option<KernelClass>,
159    ) -> Option<SecurityContext> {
160        self.0.genfscon_label_for_fs_and_path(fs_type, node_path, class_id)
161    }
162
163    /// Returns the [`SecurityContext`] defined by this policy for the specified
164    /// well-known (or "initial") Id.
165    pub fn initial_context(&self, id: crate::InitialSid) -> security_context::SecurityContext {
166        self.0.initial_context(id)
167    }
168
169    /// Returns a [`SecurityContext`] with fields parsed from the supplied Security Context string.
170    pub fn parse_security_context(
171        &self,
172        security_context: NullessByteStr<'_>,
173    ) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
174        security_context::SecurityContext::from_string(&self.0, security_context)
175    }
176
177    /// Validates a [`SecurityContext`] against this policy's constraints.
178    pub fn validate_security_context(
179        &self,
180        security_context: &SecurityContext,
181    ) -> Result<(), SecurityContextError> {
182        security_context.validate(&self.0)
183    }
184
185    /// Returns a byte string describing the supplied [`SecurityContext`].
186    pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
187        security_context.to_string(&self.0)
188    }
189
190    /// Returns the security context that should be applied to a newly created SELinux
191    /// object according to `source` and `target` security contexts, as well as the new object's
192    /// `class` and `name`.
193    ///
194    /// Computation follows the "create" algorithm for labeling newly created objects:
195    /// - user is taken from the `source` by default, or `target` if specified by policy.
196    /// - role, type and range are taken from the matching transition rules, if any.
197    /// - role, type and range fall-back to the `source` or `target` values according to policy.
198    ///
199    /// Callers pass an empty slice (`&[]`) for `name` to express nameless transitions.
200    /// When a non-empty `name` is provided, filename transition rules are checked first.
201    /// If no transitions apply, and the policy does not explicitly specify defaults then the
202    /// role, type and range values have defaults chosen based on the `class`:
203    /// - For "process", and socket-like classes, role, type and range are taken from the `source`.
204    /// - Otherwise role is "object_r", type is taken from `target` and range is set to the
205    ///   low level of the `source` range.
206    ///
207    /// Returns an error if the Security Context for such an object is not valid under this
208    /// [`Policy`] (e.g. if the type is not permitted for the chosen role, etc).
209    pub fn compute_create_context(
210        &self,
211        source: &SecurityContext,
212        target: &SecurityContext,
213        class: impl Into<ObjectClass>,
214        name: &[u8],
215    ) -> SecurityContext {
216        self.0.compute_create_context(source, target, class.into(), name)
217    }
218
219    /// Computes the access vector that associates type `source_type_name` and
220    /// `target_type_name` via an explicit `allow [...];` statement in the
221    /// binary policy, subject to any matching constraint statements. Computes
222    /// `AccessVector::NONE` if no such statement exists.
223    ///
224    /// Access decisions are currently based on explicit "allow" rules and
225    /// "constrain" or "mlsconstrain" statements. A permission is allowed if
226    /// it is allowed by an explicit "allow", and if in addition, all matching
227    /// constraints are satisfied.
228    pub fn compute_access_decision(
229        &self,
230        source_context: &SecurityContext,
231        target_context: &SecurityContext,
232        object_class: impl Into<ObjectClass>,
233    ) -> AccessDecision {
234        if let Some(target_class) = self.0.class(object_class.into()) {
235            self.0.compute_access_decision(source_context, target_context, &target_class)
236        } else {
237            let mut decision = AccessDecision::allow(AccessVector::NONE);
238            if self.is_permissive(source_context.type_()) {
239                decision.flags |= SELINUX_AVD_FLAGS_PERMISSIVE;
240            }
241            decision
242        }
243    }
244
245    /// Computes the extended permissions that should be allowed, audited when allowed, and audited
246    /// when denied, for a given kind of extended permissions (`ioctl` or `nlmsg`), source context,
247    /// target context, target class, and xperms prefix byte.
248    pub fn compute_xperms_access_decision(
249        &self,
250        xperms_kind: XpermsKind,
251        source_context: &SecurityContext,
252        target_context: &SecurityContext,
253        object_class: impl Into<ObjectClass>,
254        xperms_prefix: u8,
255    ) -> XpermsAccessDecision {
256        if let Some(target_class) = self.0.class(object_class.into()) {
257            self.0.compute_xperms_access_decision(
258                xperms_kind,
259                source_context,
260                target_context,
261                &target_class,
262                xperms_prefix,
263            )
264        } else {
265            XpermsAccessDecision::DENY_ALL
266        }
267    }
268
269    pub fn is_bounded_by(&self, bounded_type: TypeId, parent_type: TypeId) -> bool {
270        self.0.types().get_by_id(bounded_type).unwrap().bounded_by() == Some(parent_type)
271    }
272
273    /// Returns true if the policy has the marked the type/domain for permissive checks.
274    pub fn is_permissive(&self, type_: TypeId) -> bool {
275        self.0.permissive_map().contains(type_)
276    }
277}
278
279impl AccessVectorComputer for Policy {
280    fn access_decision_to_kernel_access_decision(
281        &self,
282        class: KernelClass,
283        av: AccessDecision,
284    ) -> KernelAccessDecision {
285        let mut kernel_allow;
286        let mut kernel_audit;
287        // Set the default values of the bits as appropriate for the policy's handle_unknown value.
288        // Bits corresponding to policy-known permissions will be overwritten.
289        if self.0.handle_unknown() == HandleUnknown::Allow {
290            // If we allow unknown permissions, a bit will be by default allowed and not audited.
291            kernel_allow = 0xffffffffu32;
292            kernel_audit = 0u32;
293        } else {
294            // Otherwise, a bit is by default audited and not allowed.
295            kernel_allow = 0u32;
296            kernel_audit = 0xffffffffu32;
297        }
298
299        let decision_allow = av.allow;
300        let decision_audit = (av.allow & av.auditallow) | (!av.allow & av.auditdeny);
301        for permission in class.permissions() {
302            if let Some(permission_access_vector) =
303                self.0.kernel_permission_to_access_vector(permission.clone())
304            {
305                // If the permission is known, set the corresponding bit according to
306                // `decision_allow` and `decision_audit`.
307                let bit = 1 << permission.id();
308                let allow = decision_allow & permission_access_vector == permission_access_vector;
309                let audit = decision_audit & permission_access_vector == permission_access_vector;
310                kernel_allow = (kernel_allow & !bit) | ((allow as u32) << permission.id());
311                kernel_audit = (kernel_audit & !bit) | ((audit as u32) << permission.id());
312            }
313        }
314        KernelAccessDecision {
315            allow: AccessVector::from(kernel_allow),
316            audit: AccessVector::from(kernel_audit),
317            flags: av.flags,
318            todo_bug: av.todo_bug,
319        }
320    }
321}
322
323/// A [`Policy`] that has been successfully parsed, but not validated.
324pub struct Unvalidated(ParsedPolicy);
325
326impl Unvalidated {
327    pub fn validate(self) -> Result<Policy, anyhow::Error> {
328        self.0.validate().context("validating parsed policy")?;
329        let index = PolicyIndex::new(self.0).context("building index")?;
330        Ok(Policy(index))
331    }
332}
333
334#[derive(Clone, Copy, Debug, PartialEq, Eq)]
335pub struct KernelAccessDecision {
336    pub allow: AccessVector,
337    pub audit: AccessVector,
338    pub flags: u32,
339    pub todo_bug: Option<NonZeroU32>,
340}
341
342/// An owner of policy information that can translate [`crate::Permission`] values into
343/// [`AccessVector`] values that are consistent with the owned policy.
344pub trait AccessVectorComputer {
345    /// Translates the given [`AccessDecision`] to a [`KernelAccessDecision`].
346    ///
347    /// The loaded policy's "handle unknown" configuration determines how `permissions`
348    /// entries not explicitly defined by the policy are handled. Allow-unknown will
349    /// result in unknown `permissions` being allowed, while they are denied (and audited)
350    /// if the policy uses deny-unknown.
351    fn access_decision_to_kernel_access_decision(
352        &self,
353        class: KernelClass,
354        av: AccessDecision,
355    ) -> KernelAccessDecision;
356}
357
358/// A data structure that can be parsed as a part of a binary policy.
359pub trait Parse: Sized {
360    /// The type of error that may be returned from `parse()`, usually [`ParseError`] or
361    /// [`anyhow::Error`].
362    type Error: Into<anyhow::Error>;
363
364    /// Parses a `Self` from `bytes`, returning the `Self` and trailing bytes, or an error if
365    /// bytes corresponding to a `Self` are malformed.
366    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error>;
367}
368
369/// Treat a type as metadata that contains a count of subsequent data.
370impl Parse for le::U32 {
371    type Error = anyhow::Error;
372
373    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
374        bytes.parse::<le::U32>().map_err(anyhow::Error::from)
375    }
376}
377
378impl<T: crate::new_policy::traits::Parse> Parse for T {
379    type Error = anyhow::Error;
380
381    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
382        let offset = bytes.offset() as usize;
383        let slice = &bytes.data().as_ref()[offset..];
384        let mut new_cursor = crate::new_policy::parser::PolicyCursor::new(slice);
385        let item = <T as crate::new_policy::traits::Parse>::parse(&mut new_cursor)
386            .map_err(|e| anyhow::anyhow!("Parse error: {:?}", e))?;
387        let new_offset = bytes.offset() + new_cursor.offset() as u32;
388        Ok((item, PolicyCursor::new_at(bytes.data(), new_offset)))
389    }
390}
391
392#[cfg(test)]
393pub(super) mod tests {
394    use super::security_context::SecurityContext;
395    use super::{
396        AccessVector, ClassId, HandleUnknown, Policy, TypeId, XpermsAccessDecision, XpermsBitmap,
397        XpermsKind, parse_policy_by_value,
398    };
399    use crate::new_policy::traits::HasPolicyId;
400    use crate::{FileClass, InitialSid, KernelClass};
401    use anyhow::Context as _;
402    use serde::Deserialize;
403
404    /// Returns whether the input types are explicitly granted `permission` via an `allow [...];`
405    /// policy statement.
406    ///
407    /// # Panics
408    /// If supplied with type Ids not previously obtained from the `Policy` itself; validation
409    /// ensures that all such Ids have corresponding definitions.
410    /// If either of `target_class` or `permission` cannot be resolved in the policy.
411    fn is_explicitly_allowed(
412        policy: &Policy,
413        source_type: TypeId,
414        target_type: TypeId,
415        target_class: &str,
416        permission: &str,
417    ) -> bool {
418        let classes = policy.classes();
419        let class = classes.get_by_name(target_class.as_bytes()).expect("class not found");
420        let class_permissions = policy
421            .find_class_permissions_by_name(target_class)
422            .expect("class permissions not found");
423        let (permission_id, _) = class_permissions
424            .iter()
425            .find(|(_, name)| permission.as_bytes() == name)
426            .expect("permission not found");
427        let permission_bit = AccessVector::from(*permission_id);
428        let access_decision = policy.0.compute_explicitly_allowed(source_type, target_type, class);
429        permission_bit == access_decision.allow & permission_bit
430    }
431
432    #[derive(Debug, Deserialize)]
433    struct Expectations {
434        expected_policy_version: u32,
435        expected_handle_unknown: LocalHandleUnknown,
436    }
437
438    #[derive(Debug, Deserialize, PartialEq)]
439    #[serde(rename_all = "snake_case")]
440    enum LocalHandleUnknown {
441        Deny,
442        Reject,
443        Allow,
444    }
445
446    impl PartialEq<HandleUnknown> for LocalHandleUnknown {
447        fn eq(&self, other: &HandleUnknown) -> bool {
448            match self {
449                LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
450                LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
451                LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
452            }
453        }
454    }
455
456    /// Given a vector of integer (u8) values, returns a bitmap in which the set bits correspond to
457    /// the indices of the provided values.
458    fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
459        let mut bitmap = [0u64; 4];
460        for element in elements {
461            let block_index = (*element as usize) / 64;
462            let bit_index = (*element as usize) % 64;
463            bitmap[block_index] |= 1u64 << bit_index;
464        }
465        XpermsBitmap::new(bitmap)
466    }
467
468    #[test]
469    fn known_policies() {
470        let policies_and_expectations = [
471            [
472                b"testdata/policies/emulator".to_vec(),
473                include_bytes!("../../testdata/policies/emulator").to_vec(),
474                include_bytes!("../../testdata/expectations/emulator").to_vec(),
475            ],
476            [
477                b"testdata/policies/selinux_testsuite".to_vec(),
478                include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
479                include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
480            ],
481        ];
482
483        for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
484            let expectations = serde_json5::from_reader::<_, Expectations>(
485                &mut std::io::Cursor::new(expectations_bytes),
486            )
487            .expect("deserialize expectations");
488
489            // Test parse-by-value.
490
491            let unvalidated_policy =
492                parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
493
494            let policy = unvalidated_policy
495                .validate()
496                .with_context(|| {
497                    format!(
498                        "policy path: {:?}",
499                        std::str::from_utf8(policy_path.as_slice()).unwrap()
500                    )
501                })
502                .expect("validate policy");
503
504            assert_eq!(expectations.expected_policy_version, policy.version().get());
505            assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
506
507            // Returned policy bytes must be identical to input policy bytes.
508            let binary_policy = policy.serialize();
509            assert_eq!(policy_bytes, binary_policy.as_ref());
510        }
511    }
512
513    #[test]
514    fn policy_lookup() {
515        let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
516        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
517        let policy = policy.validate().expect("validate selinux testsuite policy");
518
519        let unconfined_t = policy.types().get_by_name(b"unconfined_t").expect("look up type").id();
520
521        assert!(is_explicitly_allowed(&policy, unconfined_t, unconfined_t, "process", "fork",));
522    }
523
524    #[test]
525    fn initial_contexts() {
526        let policy_bytes =
527            include_bytes!("../../testdata/micro_policies/multiple_levels_and_categories_policy");
528        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
529        let policy = policy.validate().expect("validate policy");
530
531        let kernel_context = policy.initial_context(InitialSid::Kernel);
532        assert_eq!(
533            policy.serialize_security_context(&kernel_context),
534            b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
535        )
536    }
537
538    #[test]
539    fn explicit_allow_type_type() {
540        let policy_bytes =
541            include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy");
542        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
543        let policy = policy.validate().expect("validate policy");
544
545        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
546        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
547
548        assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
549    }
550
551    #[test]
552    fn no_explicit_allow_type_type() {
553        let policy_bytes =
554            include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy");
555        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
556        let policy = policy.validate().expect("validate policy");
557
558        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
559        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
560
561        assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
562    }
563
564    #[test]
565    fn explicit_allow_type_attr() {
566        let policy_bytes =
567            include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy");
568        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
569        let policy = policy.validate().expect("validate policy");
570
571        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
572        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
573
574        assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
575    }
576
577    #[test]
578    fn no_explicit_allow_type_attr() {
579        let policy_bytes =
580            include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy");
581        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
582        let policy = policy.validate().expect("validate policy");
583
584        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
585        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
586
587        assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
588    }
589
590    #[test]
591    fn explicit_allow_attr_attr() {
592        let policy_bytes =
593            include_bytes!("../../testdata/micro_policies/allow_a_attr_b_attr_class0_perm0_policy");
594        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
595        let policy = policy.validate().expect("validate policy");
596
597        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
598        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
599
600        assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
601    }
602
603    #[test]
604    fn no_explicit_allow_attr_attr() {
605        let policy_bytes = include_bytes!(
606            "../../testdata/micro_policies/no_allow_a_attr_b_attr_class0_perm0_policy"
607        );
608        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
609        let policy = policy.validate().expect("validate policy");
610
611        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
612        let b_t = policy.types().get_by_name(b"b_t").expect("look up type").id();
613
614        assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
615    }
616
617    #[test]
618    fn compute_explicitly_allowed_multiple_attributes() {
619        let policy_bytes = include_bytes!(
620            "../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy"
621        );
622        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
623        let policy = policy.validate().expect("validate policy");
624
625        let a_t = policy.types().get_by_name(b"a_t").expect("look up type").id();
626
627        let classes = policy.classes();
628        let class = classes.get_by_name(b"class0").expect("class not found");
629        let raw_access_vector = policy.0.compute_explicitly_allowed(a_t, a_t, class).allow.value();
630
631        // Two separate attributes are each allowed one permission on `[attr] self:class0`. Both
632        // attributes are associated with "a_t". No other `allow` statements appear in the policy
633        // in relation to "a_t". Therefore, we expect exactly two 1's in the access vector for
634        // query `("a_t", "a_t", "class0")`.
635        assert_eq!(2, raw_access_vector.count_ones());
636    }
637
638    #[test]
639    fn compute_access_decision_with_constraints() {
640        let policy_bytes =
641            include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy");
642        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
643        let policy = policy.validate().expect("validate policy");
644
645        let source_context: SecurityContext = policy
646            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
647            .expect("create source security context");
648
649        let target_context_satisfied: SecurityContext = source_context.clone();
650        let decision_satisfied = policy.compute_access_decision(
651            &source_context,
652            &target_context_satisfied,
653            KernelClass::File,
654        );
655        // The class `file` has 4 permissions, 3 of which are explicitly
656        // allowed for this target context. All of those permissions satisfy all
657        // matching constraints.
658        assert_eq!(decision_satisfied.allow, AccessVector::from(7));
659
660        let target_context_unsatisfied: SecurityContext = policy
661            .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
662            .expect("create target security context failing some constraints");
663        let decision_unsatisfied = policy.compute_access_decision(
664            &source_context,
665            &target_context_unsatisfied,
666            KernelClass::File,
667        );
668        // Two of the explicitly-allowed permissions fail to satisfy a matching
669        // constraint. Only 1 is allowed in the final access decision.
670        assert_eq!(decision_unsatisfied.allow, AccessVector::from(4));
671    }
672
673    #[test]
674    fn compute_ioctl_access_decision_explicitly_allowed() {
675        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
676        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
677        let policy = policy.validate().expect("validate policy");
678
679        let source_context: SecurityContext = policy
680            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
681            .expect("create source security context");
682        let target_context_matched: SecurityContext = source_context.clone();
683
684        // `allowxperm` rules for the `file` class:
685        //
686        // `allowxperm type0 self:file ioctl { 0xabcd };`
687        // `allowxperm type0 self:file ioctl { 0xabef };`
688        // `allowxperm type0 self:file ioctl { 0x1000 - 0x10ff };`
689        //
690        // `auditallowxperm` rules for the `file` class:
691        //
692        // auditallowxperm type0 self:file ioctl { 0xabcd };
693        // auditallowxperm type0 self:file ioctl { 0xabef };
694        // auditallowxperm type0 self:file ioctl { 0x1000 - 0x10ff };
695        //
696        // `dontauditxperm` rules for the `file` class:
697        //
698        // dontauditxperm type0 self:file ioctl { 0xabcd };
699        // dontauditxperm type0 self:file ioctl { 0xabef };
700        // dontauditxperm type0 self:file ioctl { 0x1000 - 0x10ff };
701        let decision_single = policy.compute_xperms_access_decision(
702            XpermsKind::Ioctl,
703            &source_context,
704            &target_context_matched,
705            KernelClass::File,
706            0xab,
707        );
708
709        let mut expected_auditdeny =
710            xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
711        expected_auditdeny -= xperms_bitmap_from_elements(&[0xcd, 0xef]);
712
713        let expected_decision_single = XpermsAccessDecision {
714            allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
715            auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
716            auditdeny: expected_auditdeny,
717        };
718        assert_eq!(decision_single, expected_decision_single);
719
720        let decision_range = policy.compute_xperms_access_decision(
721            XpermsKind::Ioctl,
722            &source_context,
723            &target_context_matched,
724            KernelClass::File,
725            0x10,
726        );
727        let expected_decision_range = XpermsAccessDecision {
728            allow: XpermsBitmap::ALL,
729            auditallow: XpermsBitmap::ALL,
730            auditdeny: XpermsBitmap::NONE,
731        };
732        assert_eq!(decision_range, expected_decision_range);
733    }
734
735    #[test]
736    fn compute_ioctl_access_decision_denied() {
737        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
738        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
739        let class_id = unvalidated
740            .0
741            .classes()
742            .get_by_name(b"class_one_ioctl")
743            .expect("look up class_one_ioctl")
744            .id();
745        let policy = unvalidated.validate().expect("validate policy");
746        let source_context: SecurityContext = policy
747            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
748            .expect("create source security context");
749        let target_context_matched: SecurityContext = source_context.clone();
750
751        // `allowxperm` rules for the `class_one_ioctl` class:
752        //
753        // `allowxperm type0 self:class_one_ioctl ioctl { 0xabcd };`
754        let decision_single = policy.compute_xperms_access_decision(
755            XpermsKind::Ioctl,
756            &source_context,
757            &target_context_matched,
758            class_id,
759            0xdb,
760        );
761
762        let expected_decision = XpermsAccessDecision {
763            allow: XpermsBitmap::NONE,
764            auditallow: XpermsBitmap::NONE,
765            auditdeny: XpermsBitmap::ALL,
766        };
767        assert_eq!(decision_single, expected_decision);
768    }
769
770    #[test]
771    fn compute_ioctl_access_decision_unmatched() {
772        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
773        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
774        let policy = policy.validate().expect("validate policy");
775
776        let source_context: SecurityContext = policy
777            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
778            .expect("create source security context");
779
780        // No matching ioctl xperm-related statements for this target's type
781        let target_context_unmatched: SecurityContext = policy
782            .parse_security_context(b"user0:object_r:type1:s0-s0".into())
783            .expect("create source security context");
784
785        for prefix in 0x0..=0xff {
786            let decision = policy.compute_xperms_access_decision(
787                XpermsKind::Ioctl,
788                &source_context,
789                &target_context_unmatched,
790                KernelClass::File,
791                prefix,
792            );
793            assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
794        }
795    }
796
797    #[test]
798    fn compute_ioctl_earlier_redundant_prefixful_not_coalesced_into_prefixless() {
799        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
800        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
801        let class_id = unvalidated
802            .0
803            .classes()
804            .get_by_name(b"class_earlier_redundant_prefixful_not_coalesced_into_prefixless")
805            .expect("look up class_earlier_redundant_prefixful_not_coalesced_into_prefixless")
806            .id();
807        let policy = unvalidated.validate().expect("validate policy");
808        let source_context: SecurityContext = policy
809            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
810            .expect("create source security context");
811        let target_context_matched: SecurityContext = source_context.clone();
812
813        // `allowxperm` rules for the `class_earlier_redundant_prefixful_not_coalesced_into_prefixless` class:
814        //
815        // `allowxperm type0 self:class_earlier_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x8001-0x8002 };`
816        // `allowxperm type0 self:class_earlier_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x8000-0x80ff };`
817        let decision = policy.compute_xperms_access_decision(
818            XpermsKind::Ioctl,
819            &source_context,
820            &target_context_matched,
821            class_id,
822            0x7f,
823        );
824        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
825        let decision = policy.compute_xperms_access_decision(
826            XpermsKind::Ioctl,
827            &source_context,
828            &target_context_matched,
829            class_id,
830            0x80,
831        );
832        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
833        let decision = policy.compute_xperms_access_decision(
834            XpermsKind::Ioctl,
835            &source_context,
836            &target_context_matched,
837            class_id,
838            0x81,
839        );
840        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
841    }
842
843    #[test]
844    fn compute_ioctl_later_redundant_prefixful_not_coalesced_into_prefixless() {
845        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
846        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
847        let class_id = unvalidated
848            .0
849            .classes()
850            .get_by_name(b"class_later_redundant_prefixful_not_coalesced_into_prefixless")
851            .expect("look up class_later_redundant_prefixful_not_coalesced_into_prefixless")
852            .id();
853        let policy = unvalidated.validate().expect("validate policy");
854        let source_context: SecurityContext = policy
855            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
856            .expect("create source security context");
857        let target_context_matched: SecurityContext = source_context.clone();
858
859        // `allowxperm` rules for the `class_later_redundant_prefixful_not_coalesced_into_prefixless` class:
860        //
861        // `allowxperm type0 self:class_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x9000-0x90ff };`
862        // `allowxperm type0 self:class_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0x90fd-0x90fe };`
863        let decision = policy.compute_xperms_access_decision(
864            XpermsKind::Ioctl,
865            &source_context,
866            &target_context_matched,
867            class_id,
868            0x8f,
869        );
870        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
871        let decision = policy.compute_xperms_access_decision(
872            XpermsKind::Ioctl,
873            &source_context,
874            &target_context_matched,
875            class_id,
876            0x90,
877        );
878        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
879        let decision = policy.compute_xperms_access_decision(
880            XpermsKind::Ioctl,
881            &source_context,
882            &target_context_matched,
883            class_id,
884            0x91,
885        );
886        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
887    }
888
889    #[test]
890    fn compute_ioctl_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless() {
891        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
892        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
893        let class_id = unvalidated
894            .0
895            .classes()
896            .get_by_name(
897                b"class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless",
898            )
899            .expect(
900                "look up class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless",
901            )
902            .id();
903        let policy = unvalidated.validate().expect("validate policy");
904        let source_context: SecurityContext = policy
905            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
906            .expect("create source security context");
907        let target_context_matched: SecurityContext = source_context.clone();
908
909        // `allowxperm` rules for the `class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless` class:
910        //
911        // `allowxperm type0 self:class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0xa001-0xa002 };`
912        // `allowxperm type0 self:class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0xa000-0xa03f 0xa040-0xa0ff };`
913        // `allowxperm type0 self:class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless ioctl { 0xa0fd-0xa0fe };`
914        let decision = policy.compute_xperms_access_decision(
915            XpermsKind::Ioctl,
916            &source_context,
917            &target_context_matched,
918            class_id,
919            0x9f,
920        );
921        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
922        let decision = policy.compute_xperms_access_decision(
923            XpermsKind::Ioctl,
924            &source_context,
925            &target_context_matched,
926            class_id,
927            0xa0,
928        );
929        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
930        let decision = policy.compute_xperms_access_decision(
931            XpermsKind::Ioctl,
932            &source_context,
933            &target_context_matched,
934            class_id,
935            0xa1,
936        );
937        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
938    }
939
940    #[test]
941    fn compute_ioctl_prefixfuls_that_coalesce_to_prefixless() {
942        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
943        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
944        let class_id: ClassId = unvalidated
945            .0
946            .classes()
947            .get_by_name(b"class_prefixfuls_that_coalesce_to_prefixless")
948            .expect("look up class_prefixfuls_that_coalesce_to_prefixless")
949            .id();
950        let policy = unvalidated.validate().expect("validate policy");
951        let source_context: SecurityContext = policy
952            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
953            .expect("create source security context");
954        let target_context_matched: SecurityContext = source_context.clone();
955
956        // `allowxperm` rules for the `class_prefixfuls_that_coalesce_to_prefixless` class:
957        //
958        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless ioctl { 0xb000 0xb001 0xb002 };`
959        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless ioctl { 0xb003-0xb0fc };`
960        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless ioctl { 0xb0fd 0xb0fe 0xb0ff };`
961        let decision = policy.compute_xperms_access_decision(
962            XpermsKind::Ioctl,
963            &source_context,
964            &target_context_matched,
965            class_id,
966            0xaf,
967        );
968        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
969        let decision = policy.compute_xperms_access_decision(
970            XpermsKind::Ioctl,
971            &source_context,
972            &target_context_matched,
973            class_id,
974            0xb0,
975        );
976        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
977        let decision = policy.compute_xperms_access_decision(
978            XpermsKind::Ioctl,
979            &source_context,
980            &target_context_matched,
981            class_id,
982            0xb1,
983        );
984        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
985    }
986
987    #[test]
988    fn compute_ioctl_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless() {
989        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
990        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
991        let class_id = unvalidated
992            .0
993            .classes()
994            .get_by_name(b"class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless")
995            .expect("look up class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless")
996            .id();
997        let policy = unvalidated.validate().expect("validate policy");
998        let source_context: SecurityContext = policy
999            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1000            .expect("create source security context");
1001        let target_context_matched: SecurityContext = source_context.clone();
1002
1003        // `allowxperm` rules for the `class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless` class:
1004        //
1005        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc000 0xc001 0xc002 0xc003 };`
1006        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc004-0xc0fb };`
1007        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc0fc 0xc0fd 0xc0fe 0xc0ff };`
1008        // `allowxperm type0 self:class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless ioctl { 0xc100-0xc1ff };`
1009        let decision = policy.compute_xperms_access_decision(
1010            XpermsKind::Ioctl,
1011            &source_context,
1012            &target_context_matched,
1013            class_id,
1014            0xbf,
1015        );
1016        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1017        let decision = policy.compute_xperms_access_decision(
1018            XpermsKind::Ioctl,
1019            &source_context,
1020            &target_context_matched,
1021            class_id,
1022            0xc0,
1023        );
1024        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1025        let decision = policy.compute_xperms_access_decision(
1026            XpermsKind::Ioctl,
1027            &source_context,
1028            &target_context_matched,
1029            class_id,
1030            0xc1,
1031        );
1032        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1033        let decision = policy.compute_xperms_access_decision(
1034            XpermsKind::Ioctl,
1035            &source_context,
1036            &target_context_matched,
1037            class_id,
1038            0xc2,
1039        );
1040        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1041    }
1042
1043    #[test]
1044    fn compute_ioctl_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless() {
1045        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1046        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1047        let class_id = unvalidated
1048            .0
1049            .classes()
1050            .get_by_name(b"class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless")
1051            .expect("look up class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless")
1052            .id();
1053        let policy = unvalidated.validate().expect("validate policy");
1054        let source_context: SecurityContext = policy
1055            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1056            .expect("create source security context");
1057        let target_context_matched: SecurityContext = source_context.clone();
1058
1059        // `allowxperm` rules for the `class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless` class:
1060        //
1061        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd600-0xd6ff };`
1062        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd700 0xd701 0xd702 0xd703 };`
1063        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd704-0xd7fb };`
1064        // `allowxperm type0 self:class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless ioctl { 0xd7fc 0xd7fd 0xd7fe 0xd7ff };`
1065        let decision = policy.compute_xperms_access_decision(
1066            XpermsKind::Ioctl,
1067            &source_context,
1068            &target_context_matched,
1069            class_id,
1070            0xd5,
1071        );
1072        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1073        let decision = policy.compute_xperms_access_decision(
1074            XpermsKind::Ioctl,
1075            &source_context,
1076            &target_context_matched,
1077            class_id,
1078            0xd6,
1079        );
1080        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1081        let decision = policy.compute_xperms_access_decision(
1082            XpermsKind::Ioctl,
1083            &source_context,
1084            &target_context_matched,
1085            class_id,
1086            0xd7,
1087        );
1088        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1089        let decision = policy.compute_xperms_access_decision(
1090            XpermsKind::Ioctl,
1091            &source_context,
1092            &target_context_matched,
1093            class_id,
1094            0xd8,
1095        );
1096        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1097    }
1098
1099    // As of 2025-12, the policy compiler generates allow rules in an unexpected order in the
1100    // policy binary for this oddly-expressed policy text content (with one "prefixful" rule
1101    // of type [`XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES`], then the "prefixless" rule of type
1102    // `XPERMS_TYPE_IOCTL_PREFIXES`, and then two more rules of type
1103    // `XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES`). These rules are still contiguous and without
1104    // interruption by rules of other source-target-class-type quadruplets; it's just unexpected
1105    // that the "prefixless" one falls in the middle of the "prefixful" ones rather than
1106    // consistently at the beginning or the end of the "prefixful" ones. We don't directly test
1107    // that our odd text content leads to this curious binary content, but we do test that we
1108    // make correct access decisions.
1109    #[test]
1110    fn compute_ioctl_ridiculous_permission_ordering() {
1111        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1112        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1113        let class_id = unvalidated
1114            .0
1115            .classes()
1116            .get_by_name(b"class_ridiculous_permission_ordering")
1117            .expect("look up class_ridiculous_permission_ordering")
1118            .id();
1119        let policy = unvalidated.validate().expect("validate policy");
1120        let source_context: SecurityContext = policy
1121            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1122            .expect("create source security context");
1123        let target_context_matched: SecurityContext = source_context.clone();
1124
1125        // `allowxperm` rules for the `class_ridiculous_permission_ordering` class:
1126        //
1127        // `allowxperm type0 self:class_ridiculous_permission_ordering ioctl { 0xfdfa-0xfdfd 0xf001 };`
1128        // `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 };`
1129        let decision = policy.compute_xperms_access_decision(
1130            XpermsKind::Ioctl,
1131            &source_context,
1132            &target_context_matched,
1133            class_id,
1134            0x00,
1135        );
1136        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1137        let decision = policy.compute_xperms_access_decision(
1138            XpermsKind::Ioctl,
1139            &source_context,
1140            &target_context_matched,
1141            class_id,
1142            0x01,
1143        );
1144        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1145        let decision = policy.compute_xperms_access_decision(
1146            XpermsKind::Ioctl,
1147            &source_context,
1148            &target_context_matched,
1149            class_id,
1150            0xbf,
1151        );
1152        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1153        let decision = policy.compute_xperms_access_decision(
1154            XpermsKind::Ioctl,
1155            &source_context,
1156            &target_context_matched,
1157            class_id,
1158            0xc0,
1159        );
1160        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1161        let decision = policy.compute_xperms_access_decision(
1162            XpermsKind::Ioctl,
1163            &source_context,
1164            &target_context_matched,
1165            class_id,
1166            0xce,
1167        );
1168        assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1169        let decision = policy.compute_xperms_access_decision(
1170            XpermsKind::Ioctl,
1171            &source_context,
1172            &target_context_matched,
1173            class_id,
1174            0xcf,
1175        );
1176        assert_eq!(
1177            decision,
1178            XpermsAccessDecision {
1179                allow: xperms_bitmap_from_elements((0x0..=0xf2).collect::<Vec<_>>().as_slice()),
1180                auditallow: XpermsBitmap::NONE,
1181                auditdeny: XpermsBitmap::ALL,
1182            }
1183        );
1184        let decision = policy.compute_xperms_access_decision(
1185            XpermsKind::Ioctl,
1186            &source_context,
1187            &target_context_matched,
1188            class_id,
1189            0xd0,
1190        );
1191        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1192        let decision = policy.compute_xperms_access_decision(
1193            XpermsKind::Ioctl,
1194            &source_context,
1195            &target_context_matched,
1196            class_id,
1197            0xe9,
1198        );
1199        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1200        let decision = policy.compute_xperms_access_decision(
1201            XpermsKind::Ioctl,
1202            &source_context,
1203            &target_context_matched,
1204            class_id,
1205            0xf0,
1206        );
1207        assert_eq!(
1208            decision,
1209            XpermsAccessDecision {
1210                allow: xperms_bitmap_from_elements(&[0x01]),
1211                auditallow: XpermsBitmap::NONE,
1212                auditdeny: XpermsBitmap::ALL,
1213            }
1214        );
1215        let decision = policy.compute_xperms_access_decision(
1216            XpermsKind::Ioctl,
1217            &source_context,
1218            &target_context_matched,
1219            class_id,
1220            0xf1,
1221        );
1222        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1223        let decision = policy.compute_xperms_access_decision(
1224            XpermsKind::Ioctl,
1225            &source_context,
1226            &target_context_matched,
1227            class_id,
1228            0xfc,
1229        );
1230        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1231        let decision = policy.compute_xperms_access_decision(
1232            XpermsKind::Ioctl,
1233            &source_context,
1234            &target_context_matched,
1235            class_id,
1236            0xfd,
1237        );
1238        assert_eq!(
1239            decision,
1240            XpermsAccessDecision {
1241                allow: xperms_bitmap_from_elements((0xfa..=0xfd).collect::<Vec<_>>().as_slice()),
1242                auditallow: XpermsBitmap::NONE,
1243                auditdeny: XpermsBitmap::ALL,
1244            }
1245        );
1246        let decision = policy.compute_xperms_access_decision(
1247            XpermsKind::Ioctl,
1248            &source_context,
1249            &target_context_matched,
1250            class_id,
1251            0xfe,
1252        );
1253        assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1254    }
1255
1256    #[test]
1257    fn compute_nlmsg_access_decision_explicitly_allowed() {
1258        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1259        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1260        let policy = policy.validate().expect("validate policy");
1261
1262        let source_context: SecurityContext = policy
1263            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1264            .expect("create source security context");
1265        let target_context_matched: SecurityContext = source_context.clone();
1266
1267        // `allowxperm` rules for the `netlink_route_socket` class:
1268        //
1269        // `allowxperm type0 self:netlink_route_socket nlmsg { 0xabcd };`
1270        // `allowxperm type0 self:netlink_route_socket nlmsg { 0xabef };`
1271        // `allowxperm type0 self:netlink_route_socket nlmsg { 0x1000 - 0x10ff };`
1272        //
1273        // `auditallowxperm` rules for the `netlink_route_socket` class:
1274        //
1275        // auditallowxperm type0 self:netlink_route_socket nlmsg { 0xabcd };
1276        // auditallowxperm type0 self:netlink_route_socket nlmsg { 0xabef };
1277        // auditallowxperm type0 self:netlink_route_socket nlmsg { 0x1000 - 0x10ff };
1278        //
1279        // `dontauditxperm` rules for the `netlink_route_socket` class:
1280        //
1281        // dontauditxperm type0 self:netlink_route_socket nlmsg { 0xabcd };
1282        // dontauditxperm type0 self:netlink_route_socket nlmsg { 0xabef };
1283        // dontauditxperm type0 self:netlink_route_socket nlmsg { 0x1000 - 0x10ff };
1284        let decision_single = policy.compute_xperms_access_decision(
1285            XpermsKind::Nlmsg,
1286            &source_context,
1287            &target_context_matched,
1288            KernelClass::NetlinkRouteSocket,
1289            0xab,
1290        );
1291
1292        let mut expected_auditdeny =
1293            xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1294        expected_auditdeny -= xperms_bitmap_from_elements(&[0xcd, 0xef]);
1295
1296        let expected_decision_single = XpermsAccessDecision {
1297            allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1298            auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1299            auditdeny: expected_auditdeny,
1300        };
1301        assert_eq!(decision_single, expected_decision_single);
1302
1303        let decision_range = policy.compute_xperms_access_decision(
1304            XpermsKind::Nlmsg,
1305            &source_context,
1306            &target_context_matched,
1307            KernelClass::NetlinkRouteSocket,
1308            0x10,
1309        );
1310        let expected_decision_range = XpermsAccessDecision {
1311            allow: XpermsBitmap::ALL,
1312            auditallow: XpermsBitmap::ALL,
1313            auditdeny: XpermsBitmap::NONE,
1314        };
1315        assert_eq!(decision_range, expected_decision_range);
1316    }
1317
1318    #[test]
1319    fn compute_nlmsg_access_decision_unmatched() {
1320        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1321        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1322        let policy = policy.validate().expect("validate policy");
1323
1324        let source_context: SecurityContext = policy
1325            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1326            .expect("create source security context");
1327
1328        // No matching nlmsg xperm-related statements for this target's type
1329        let target_context_unmatched: SecurityContext = policy
1330            .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1331            .expect("create source security context");
1332
1333        for prefix in 0x0..=0xff {
1334            let decision = policy.compute_xperms_access_decision(
1335                XpermsKind::Nlmsg,
1336                &source_context,
1337                &target_context_unmatched,
1338                KernelClass::NetlinkRouteSocket,
1339                prefix,
1340            );
1341            assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1342        }
1343    }
1344
1345    #[test]
1346    fn compute_ioctl_grant_does_not_cause_nlmsg_deny() {
1347        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1348        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1349        let class_id = unvalidated
1350            .0
1351            .classes()
1352            .get_by_name(b"class_ioctl_grant_does_not_cause_nlmsg_deny")
1353            .expect("look up class_ioctl_grant_does_not_cause_nlmsg_deny")
1354            .id();
1355        let policy = unvalidated.validate().expect("validate policy");
1356        let source_context: SecurityContext = policy
1357            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1358            .expect("create source security context");
1359        let target_context_matched: SecurityContext = source_context.clone();
1360
1361        // `allowxperm` rules for the `class_ioctl_grant_does_not_cause_nlmsg_deny` class:
1362        //
1363        // `allowxperm type0 self:class_ioctl_grant_does_not_cause_nlmsg_deny ioctl { 0x0002 };`
1364        let ioctl_decision = policy.compute_xperms_access_decision(
1365            XpermsKind::Ioctl,
1366            &source_context,
1367            &target_context_matched,
1368            class_id,
1369            0x00,
1370        );
1371        assert_eq!(
1372            ioctl_decision,
1373            XpermsAccessDecision {
1374                allow: xperms_bitmap_from_elements(&[0x0002]),
1375                auditallow: XpermsBitmap::NONE,
1376                auditdeny: XpermsBitmap::ALL,
1377            }
1378        );
1379        let nlmsg_decision = policy.compute_xperms_access_decision(
1380            XpermsKind::Nlmsg,
1381            &source_context,
1382            &target_context_matched,
1383            class_id,
1384            0x00,
1385        );
1386        assert_eq!(nlmsg_decision, XpermsAccessDecision::ALLOW_ALL);
1387    }
1388
1389    #[test]
1390    fn compute_nlmsg_grant_does_not_cause_ioctl_deny() {
1391        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1392        let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1393        let class_id = unvalidated
1394            .0
1395            .classes()
1396            .get_by_name(b"class_nlmsg_grant_does_not_cause_ioctl_deny")
1397            .expect("look up class_nlmsg_grant_does_not_cause_ioctl_deny")
1398            .id();
1399        let policy = unvalidated.validate().expect("validate policy");
1400        let source_context: SecurityContext = policy
1401            .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1402            .expect("create source security context");
1403        let target_context_matched: SecurityContext = source_context.clone();
1404
1405        // `allowxperm` rules for the `class_nlmsg_grant_does_not_cause_ioctl_deny` class:
1406        //
1407        // `allowxperm type0 self:class_nlmsg_grant_does_not_cause_ioctl_deny nlmsg { 0x0003 };`
1408        let nlmsg_decision = policy.compute_xperms_access_decision(
1409            XpermsKind::Nlmsg,
1410            &source_context,
1411            &target_context_matched,
1412            class_id,
1413            0x00,
1414        );
1415        assert_eq!(
1416            nlmsg_decision,
1417            XpermsAccessDecision {
1418                allow: xperms_bitmap_from_elements(&[0x0003]),
1419                auditallow: XpermsBitmap::NONE,
1420                auditdeny: XpermsBitmap::ALL,
1421            }
1422        );
1423        let ioctl_decision = policy.compute_xperms_access_decision(
1424            XpermsKind::Ioctl,
1425            &source_context,
1426            &target_context_matched,
1427            class_id,
1428            0x00,
1429        );
1430        assert_eq!(ioctl_decision, XpermsAccessDecision::ALLOW_ALL);
1431    }
1432
1433    #[test]
1434    fn compute_create_context_minimal() {
1435        let policy_bytes =
1436            include_bytes!("../../testdata/composite_policies/compiled/minimal_policy");
1437        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1438        let policy = policy.validate().expect("validate policy");
1439        let source = policy
1440            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1441            .expect("valid source security context");
1442        let target = policy
1443            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1444            .expect("valid target security context");
1445
1446        let actual = policy.compute_create_context(&source, &target, FileClass::File, &[]);
1447        let expected: SecurityContext = policy
1448            .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1449            .expect("valid expected security context");
1450
1451        assert_eq!(expected, actual);
1452    }
1453
1454    #[test]
1455    fn new_security_context_minimal() {
1456        let policy_bytes =
1457            include_bytes!("../../testdata/composite_policies/compiled/minimal_policy");
1458        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1459        let policy = policy.validate().expect("validate policy");
1460        let source = policy
1461            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1462            .expect("valid source security context");
1463        let target = policy
1464            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1465            .expect("valid target security context");
1466
1467        let actual = policy.compute_create_context(&source, &target, KernelClass::Process, &[]);
1468
1469        assert_eq!(source, actual);
1470    }
1471
1472    #[test]
1473    fn compute_create_context_class_defaults() {
1474        let policy_bytes =
1475            include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy");
1476        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1477        let policy = policy.validate().expect("validate policy");
1478        let source = policy
1479            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1480            .expect("valid source security context");
1481        let target = policy
1482            .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1483            .expect("valid target security context");
1484
1485        let actual = policy.compute_create_context(&source, &target, FileClass::File, &[]);
1486        let expected: SecurityContext = policy
1487            .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1488            .expect("valid expected security context");
1489
1490        assert_eq!(expected, actual);
1491    }
1492
1493    #[test]
1494    fn new_security_context_class_defaults() {
1495        let policy_bytes =
1496            include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy");
1497        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1498        let policy = policy.validate().expect("validate policy");
1499        let source = policy
1500            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1501            .expect("valid source security context");
1502        let target = policy
1503            .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1504            .expect("valid target security context");
1505
1506        let actual = policy.compute_create_context(&source, &target, KernelClass::Process, &[]);
1507        let expected: SecurityContext = policy
1508            .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1509            .expect("valid expected security context");
1510
1511        assert_eq!(expected, actual);
1512    }
1513
1514    #[test]
1515    fn compute_create_context_role_transition() {
1516        let policy_bytes =
1517            include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy");
1518        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1519        let policy = policy.validate().expect("validate policy");
1520        let source = policy
1521            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1522            .expect("valid source security context");
1523        let target = policy
1524            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1525            .expect("valid target security context");
1526
1527        let actual = policy.compute_create_context(&source, &target, FileClass::File, &[]);
1528        let expected: SecurityContext = policy
1529            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1530            .expect("valid expected security context");
1531
1532        assert_eq!(expected, actual);
1533    }
1534
1535    #[test]
1536    fn new_security_context_role_transition() {
1537        let policy_bytes =
1538            include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy");
1539        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1540        let policy = policy.validate().expect("validate policy");
1541        let source = policy
1542            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1543            .expect("valid source security context");
1544        let target = policy
1545            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1546            .expect("valid target security context");
1547
1548        let actual = policy.compute_create_context(&source, &target, KernelClass::Process, &[]);
1549        let expected: SecurityContext = policy
1550            .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
1551            .expect("valid expected security context");
1552
1553        assert_eq!(expected, actual);
1554    }
1555
1556    #[test]
1557    fn compute_create_context_role_transition_not_allowed() {
1558        let policy_bytes = include_bytes!(
1559            "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy"
1560        );
1561        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1562        let policy = policy.validate().expect("validate policy");
1563        let source = policy
1564            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1565            .expect("valid source security context");
1566        let target = policy
1567            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1568            .expect("valid target security context");
1569
1570        let actual = policy.compute_create_context(&source, &target, FileClass::File, &[]);
1571        let expected: SecurityContext = policy
1572            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1573            .expect("valid expected security context");
1574
1575        // Role-allow rules are not checked during `compute_create_context()`; they are checked
1576        // during `compute_access_decision()` on the "process" class.
1577        assert_eq!(expected, actual);
1578        assert!(policy.validate_security_context(&actual).is_ok());
1579    }
1580
1581    #[test]
1582    fn compute_access_decision_role_allow() {
1583        // 1. With `role_transition_policy`, `allow source_r transition_r;` is present.
1584        let policy_bytes =
1585            include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy");
1586        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1587        let policy = policy.validate().expect("validate policy");
1588        let source = policy
1589            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1590            .expect("valid source security context");
1591        let target = policy
1592            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1593            .expect("valid target security context");
1594
1595        let decision_allowed =
1596            policy.compute_access_decision(&source, &target, KernelClass::Process);
1597        assert_ne!(decision_allowed.allow, AccessVector::NONE);
1598
1599        // 2. With `role_transition_not_allowed_policy`, `allow source_r transition_r;` is missing.
1600        let policy_bytes = include_bytes!(
1601            "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy"
1602        );
1603        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1604        let policy = policy.validate().expect("validate policy");
1605        let source = policy
1606            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1607            .expect("valid source security context");
1608        let target = policy
1609            .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1610            .expect("valid target security context");
1611
1612        let decision_denied =
1613            policy.compute_access_decision(&source, &target, KernelClass::Process);
1614        assert_eq!(decision_denied.allow, AccessVector::NONE);
1615    }
1616
1617    #[test]
1618    fn compute_create_context_type_transition() {
1619        let policy_bytes =
1620            include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy");
1621        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1622        let policy = policy.validate().expect("validate policy");
1623        let source = policy
1624            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1625            .expect("valid source security context");
1626        let target = policy
1627            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1628            .expect("valid target security context");
1629
1630        let actual = policy.compute_create_context(&source, &target, FileClass::File, &[]);
1631        let expected: SecurityContext = policy
1632            .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
1633            .expect("valid expected security context");
1634
1635        assert_eq!(expected, actual);
1636    }
1637
1638    #[test]
1639    fn new_security_context_type_transition() {
1640        let policy_bytes =
1641            include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy");
1642        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1643        let policy = policy.validate().expect("validate policy");
1644        let source = policy
1645            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1646            .expect("valid source security context");
1647        let target = policy
1648            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1649            .expect("valid target security context");
1650
1651        let actual = policy.compute_create_context(&source, &target, KernelClass::Process, &[]);
1652        let expected: SecurityContext = policy
1653            .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
1654            .expect("valid expected security context");
1655
1656        assert_eq!(expected, actual);
1657    }
1658
1659    #[test]
1660    fn compute_create_context_range_transition() {
1661        let policy_bytes =
1662            include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy");
1663        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1664        let policy = policy.validate().expect("validate policy");
1665        let source = policy
1666            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1667            .expect("valid source security context");
1668        let target = policy
1669            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1670            .expect("valid target security context");
1671
1672        let actual = policy.compute_create_context(&source, &target, FileClass::File, &[]);
1673        let expected: SecurityContext = policy
1674            .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
1675            .expect("valid expected security context");
1676
1677        assert_eq!(expected, actual);
1678    }
1679
1680    #[test]
1681    fn new_security_context_range_transition() {
1682        let policy_bytes =
1683            include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy");
1684        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1685        let policy = policy.validate().expect("validate policy");
1686        let source = policy
1687            .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1688            .expect("valid source security context");
1689        let target = policy
1690            .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1691            .expect("valid target security context");
1692
1693        let actual = policy.compute_create_context(&source, &target, KernelClass::Process, &[]);
1694        let expected: SecurityContext = policy
1695            .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
1696            .expect("valid expected security context");
1697
1698        assert_eq!(expected, actual);
1699    }
1700
1701    #[test]
1702    fn access_vector_formats() {
1703        assert_eq!(format!("{:x}", AccessVector::NONE), "0");
1704        assert_eq!(format!("{:x}", AccessVector::ALL), "ffffffff");
1705        assert_eq!(format!("{:?}", AccessVector::NONE), "AccessVector(00000000)");
1706        assert_eq!(format!("{:?}", AccessVector::ALL), "AccessVector(ffffffff)");
1707    }
1708
1709    #[test]
1710    fn policy_genfscon_root_path() {
1711        let policy_bytes =
1712            include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy");
1713        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1714        let policy = policy.validate().expect("validate selinux policy");
1715
1716        {
1717            let context = policy.genfscon_label_for_fs_and_path(
1718                "fs_with_path_rules".into(),
1719                "/".into(),
1720                None,
1721            );
1722            assert_eq!(
1723                policy.serialize_security_context(&context.unwrap()),
1724                b"system_u:object_r:fs_with_path_rules_t:s0"
1725            )
1726        }
1727        {
1728            let context = policy.genfscon_label_for_fs_and_path(
1729                "fs_2_with_path_rules".into(),
1730                "/".into(),
1731                None,
1732            );
1733            assert_eq!(
1734                policy.serialize_security_context(&context.unwrap()),
1735                b"system_u:object_r:fs_2_with_path_rules_t:s0"
1736            )
1737        }
1738    }
1739
1740    #[test]
1741    fn policy_genfscon_subpaths() {
1742        let policy_bytes =
1743            include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy");
1744        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1745        let policy = policy.validate().expect("validate selinux policy");
1746
1747        let path_label_expectations = [
1748            // Matching paths defined in the policy:
1749            //    /a1/    -> fs_with_path_rules_a1_t
1750            //    /a1/b/c -> fs_with_path_rules_a1_b_c_t
1751            ("/a1/", "system_u:object_r:fs_with_path_rules_a1_t:s0"),
1752            ("/a1/b", "system_u:object_r:fs_with_path_rules_a1_t:s0"),
1753            ("/a1/b/c", "system_u:object_r:fs_with_path_rules_a1_b_c_t:s0"),
1754            // Matching paths defined in the policy:
1755            //    /a2/b    -> fs_with_path_rules_a2_b_t
1756            ("/a2/", "system_u:object_r:fs_with_path_rules_t:s0"),
1757            ("/a2/b/c/d", "system_u:object_r:fs_with_path_rules_a2_b_t:s0"),
1758            // Matching paths defined in the policy:
1759            //    /a3    -> fs_with_path_rules_a3_t
1760            ("/a3/b/c/d", "system_u:object_r:fs_with_path_rules_a3_t:s0"),
1761        ];
1762        for (path, expected_label) in path_label_expectations {
1763            let context = policy.genfscon_label_for_fs_and_path(
1764                "fs_with_path_rules".into(),
1765                path.into(),
1766                None,
1767            );
1768            assert_eq!(
1769                policy.serialize_security_context(&context.unwrap()),
1770                expected_label.as_bytes()
1771            )
1772        }
1773    }
1774
1775    #[test]
1776    fn policy_genfscon_mixed_order() {
1777        let policy_bytes =
1778            include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy");
1779        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1780        let policy = policy.validate().expect("validate selinux policy");
1781
1782        let path_label_expectations = [
1783            ("/", "system_u:object_r:fs_mixed_order_t:s0"),
1784            ("/a", "system_u:object_r:fs_mixed_order_a_t:s0"),
1785            ("/a/a", "system_u:object_r:fs_mixed_order_a_a_t:s0"),
1786            ("/a/b", "system_u:object_r:fs_mixed_order_a_b_t:s0"),
1787            ("/a/b/c", "system_u:object_r:fs_mixed_order_a_b_t:s0"),
1788        ];
1789        for (path, expected_label) in path_label_expectations {
1790            let context =
1791                policy.genfscon_label_for_fs_and_path("fs_mixed_order".into(), path.into(), None);
1792            assert_eq!(
1793                policy.serialize_security_context(&context.unwrap()),
1794                expected_label.as_bytes()
1795            );
1796        }
1797    }
1798}