Skip to main content

selinux/policy/
arrays.rs

1// Copyright 2024 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
5use crate::policy::view::Hashable;
6
7use super::error::ValidateError;
8use super::extensible_bitmap::ExtensibleBitmap;
9use super::parser::{PolicyCursor, PolicyData, PolicyOffset};
10use super::symbols::{MlsLevel, MlsRange};
11use super::view::{ArrayView, HasMetadata, Walk};
12use super::{
13    AccessVector, Array, ClassId, Counted, Parse, PolicyValidationContext, RoleId, TypeId, UserId,
14    Validate, ValidateArray, array_type, array_type_validate_deref_both,
15};
16
17use anyhow::Context as _;
18use std::hash::{Hash, Hasher};
19use std::num::NonZeroU32;
20use std::ops::Shl;
21use zerocopy::{FromBytes, Immutable, KnownLayout, Unaligned, little_endian as le};
22
23pub(super) const MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY: u32 = 31;
24
25/// Mask for [`AccessVectorRuleMetadata`]'s `access_vector_rule_type` that
26/// indicates that the access vector rule's associated data is a type ID.
27pub(super) const ACCESS_VECTOR_RULE_DATA_IS_TYPE_ID_MASK: u16 = 0x070;
28/// Mask for [`AccessVectorRuleMetadata`]'s `access_vector_rule_type` that
29/// indicates that the access vector rule's associated data is an extended
30/// permission.
31pub(super) const ACCESS_VECTOR_RULE_DATA_IS_XPERM_MASK: u16 = 0x0700;
32
33/// ** Access vector rule types ***
34///
35/// Although these values each have a single bit set, they appear to be
36/// used as enum values rather than as bit masks: i.e., the policy compiler
37/// does not produce access vector rule structures that have more than
38/// one of these types.
39/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
40/// indicates that the access vector rule comes from an `allow [source]
41/// [target]:[class] { [permissions] };` policy statement.
42pub(super) const ACCESS_VECTOR_RULE_TYPE_ALLOW: u16 = 0x1;
43/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
44/// indicates that the access vector rule comes from an `auditallow [source]
45/// [target]:[class] { [permissions] };` policy statement.
46pub(super) const ACCESS_VECTOR_RULE_TYPE_AUDITALLOW: u16 = 0x2;
47/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
48/// indicates that the access vector rule comes from a `dontaudit [source]
49/// [target]:[class] { [permissions] };` policy statement.
50pub(super) const ACCESS_VECTOR_RULE_TYPE_DONTAUDIT: u16 = 0x4;
51/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
52/// indicates that the access vector rule comes from a `type_transition
53/// [source] [target]:[class] [new_type];` policy statement.
54pub(super) const ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION: u16 = 0x10;
55/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
56/// indicates that the access vector rule comes from a `type_member
57/// [source] [target]:[class] [member_type];` policy statement.
58#[allow(dead_code)]
59pub(super) const ACCESS_VECTOR_RULE_TYPE_TYPE_MEMBER: u16 = 0x20;
60/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
61/// indicates that the access vector rule comes from a `type_change
62/// [source] [target]:[class] [change_type];` policy statement.
63#[allow(dead_code)]
64pub(super) const ACCESS_VECTOR_RULE_TYPE_TYPE_CHANGE: u16 = 0x40;
65/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type`
66/// that indicates that the access vector rule comes from an
67/// `allowxperm [source] [target]:[class] [permission] {
68/// [extended_permissions] };` policy statement.
69pub(super) const ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM: u16 = 0x100;
70/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type`
71/// that indicates that the access vector rule comes from an
72/// `auditallowxperm [source] [target]:[class] [permission] {
73/// [extended_permissions] };` policy statement.
74pub(super) const ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM: u16 = 0x200;
75/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type`
76/// that indicates that the access vector rule comes from an
77/// `dontauditxperm [source] [target]:[class] [permission] {
78/// [extended_permissions] };` policy statement.
79pub(super) const ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM: u16 = 0x400;
80
81/// ** Extended permissions types ***
82///
83/// Value for [`ExtendedPermissions`] `xperms_type` that indicates
84/// that the xperms set is a proper subset of the 16-bit ioctl
85/// xperms with a given high byte value.
86pub(super) const XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES: u8 = 1;
87/// Value for [`ExtendedPermissions`] `xperms_type` that indicates
88/// that the xperms set consists of all 16-bit ioctl xperms with a
89/// given high byte, for one or more high byte values.
90pub(super) const XPERMS_TYPE_IOCTL_PREFIXES: u8 = 2;
91/// Value for [`ExtendedPermissions`] `xperms_type` that indicates
92/// that the xperms set consists of 16-bit `nlmsg` xperms with a given
93/// high byte value in common. The xperms set may be the full set of
94/// xperms with that high byte value (unlike a set of type
95/// `XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES`).
96pub(super) const XPERMS_TYPE_NLMSG: u8 = 3;
97
98#[allow(type_alias_bounds)]
99pub(super) type SimpleArray<T> = Array<le::U32, T>;
100
101impl<T: Validate> Validate for SimpleArray<T> {
102    type Error = <T as Validate>::Error;
103    /// Default implementation of `Validate` for `SimpleArray<T>`, validating individual T
104    /// objects. It assumes no internal constraints between the objects.
105    /// Override this function for types with more complex validation requirements.
106    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
107        self.data.validate(context)
108    }
109}
110
111pub(super) type SimpleArrayView<T> = ArrayView<le::U32, T>;
112
113impl<T: Validate + Parse + Walk> Validate for SimpleArrayView<T> {
114    type Error = anyhow::Error;
115
116    /// Defers to `self.data` for validation. `self.data` has access to all information, including
117    /// size stored in `self.metadata`.
118    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
119        for item in self.data().iter(&context.data) {
120            item.validate(context)?;
121        }
122        Ok(())
123    }
124}
125
126impl Counted for le::U32 {
127    fn count(&self) -> u32 {
128        self.get()
129    }
130}
131
132impl Validate for ConditionalNode {
133    type Error = anyhow::Error;
134
135    // TODO: Validate [`ConditionalNodeMetadata`].
136    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
137        Ok(())
138    }
139}
140
141array_type!(ConditionalNodeItems, ConditionalNodeMetadata, ConditionalNodeDatum);
142
143array_type_validate_deref_both!(ConditionalNodeItems);
144
145impl ValidateArray<ConditionalNodeMetadata, ConditionalNodeDatum> for ConditionalNodeItems {
146    type Error = anyhow::Error;
147
148    /// TODO: Validate internal consistency between [`ConditionalNodeMetadata`] consecutive
149    /// [`ConditionalNodeDatum`].
150    fn validate_array(
151        _context: &PolicyValidationContext,
152        _metadata: &ConditionalNodeMetadata,
153        _items: &[ConditionalNodeDatum],
154    ) -> Result<(), Self::Error> {
155        Ok(())
156    }
157}
158
159#[derive(Debug, PartialEq)]
160pub(super) struct ConditionalNode {
161    items: ConditionalNodeItems,
162    true_list: SimpleArray<AccessVectorRule>,
163    false_list: SimpleArray<AccessVectorRule>,
164}
165
166impl Parse for ConditionalNode
167where
168    ConditionalNodeItems: Parse,
169    SimpleArray<AccessVectorRule>: Parse,
170{
171    type Error = anyhow::Error;
172
173    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
174        let tail = bytes;
175
176        let (items, tail) = ConditionalNodeItems::parse(tail)
177            .map_err(Into::<anyhow::Error>::into)
178            .context("parsing conditional node items")?;
179
180        let (true_list, tail) = SimpleArray::<AccessVectorRule>::parse(tail)
181            .map_err(Into::<anyhow::Error>::into)
182            .context("parsing conditional node true list")?;
183
184        let (false_list, tail) = SimpleArray::<AccessVectorRule>::parse(tail)
185            .map_err(Into::<anyhow::Error>::into)
186            .context("parsing conditional node false list")?;
187
188        Ok((Self { items, true_list, false_list }, tail))
189    }
190}
191
192#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
193#[repr(C, packed)]
194pub(super) struct ConditionalNodeMetadata {
195    state: le::U32,
196    count: le::U32,
197}
198
199impl Counted for ConditionalNodeMetadata {
200    fn count(&self) -> u32 {
201        self.count.get()
202    }
203}
204
205impl Validate for ConditionalNodeMetadata {
206    type Error = anyhow::Error;
207
208    /// TODO: Validate [`ConditionalNodeMetadata`] internals.
209    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
210        Ok(())
211    }
212}
213
214#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
215#[repr(C, packed)]
216pub(super) struct ConditionalNodeDatum {
217    node_type: le::U32,
218    boolean: le::U32,
219}
220
221impl Validate for ConditionalNodeDatum {
222    type Error = anyhow::Error;
223
224    /// TODO: Validate sequence of [`ConditionalNodeDatum`].
225    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
226        Ok(())
227    }
228}
229
230/// An access control rule defined by a policy statement of one of the
231/// following kinds:
232/// - `allow`, `dontaudit`, `auditallow`, and `neverallow`, which specify
233///   an access vector describing a permission set.
234/// - `allowxperm`, `auditallowxperm`, `dontaudit`, which specify a set
235///   of extended permissions.
236/// - `type_transition`, `type_change`, and `type_member`, which include
237///   a type id describing a permitted new type.
238#[derive(Debug, PartialEq)]
239pub(super) struct AccessVectorRule {
240    metadata: AccessVectorRuleMetadata,
241    permission_data: PermissionData,
242}
243
244impl AccessVectorRule {
245    /// An access vector that corresponds to the `[access_vector]` in an
246    /// `allow [source] [target]:[class] [access_vector]` policy statement,
247    /// or similarly for an `auditallow` or `dontaudit` policy statement.
248    /// Return value is `None` if this access vector rule corresponds to a
249    /// different kind of policy statement.
250    pub fn access_vector(&self) -> Option<AccessVector> {
251        match &self.permission_data {
252            PermissionData::AccessVector(access_vector_raw) => {
253                Some(AccessVector(access_vector_raw.get()))
254            }
255            _ => None,
256        }
257    }
258
259    /// A numeric type id that corresponds to the `[new_type]` in a
260    /// `type_transition [source] [target]:[class] [new_type];` policy statement,
261    /// or similarly for a `type_member` or `type_change` policy statement.
262    /// Return value is `None` if this access vector rule corresponds to a
263    /// different kind of policy statement.
264    pub fn new_type(&self) -> Option<TypeId> {
265        match &self.permission_data {
266            PermissionData::NewType(new_type) => {
267                Some(TypeId(NonZeroU32::new(new_type.get().into()).unwrap()))
268            }
269            _ => None,
270        }
271    }
272
273    /// A set of extended permissions that corresponds to the `[xperms]` in an
274    /// `allowxperm [source][target]:[class] [permission] [xperms]` policy
275    /// statement, or similarly for an `auditallowxperm` or `dontauditxperm`
276    /// policy statement. Return value is `None` if this access vector rule
277    /// corresponds to a different kind of policy statement.
278    pub fn extended_permissions(&self) -> Option<&ExtendedPermissions> {
279        match &self.permission_data {
280            PermissionData::ExtendedPermissions(xperms) => Some(xperms),
281            _ => None,
282        }
283    }
284}
285
286impl Walk for AccessVectorRule {
287    fn walk(policy_data: &PolicyData, offset: PolicyOffset) -> PolicyOffset {
288        const METADATA_SIZE: u32 = std::mem::size_of::<AccessVectorRuleMetadata>() as u32;
289        let bytes = &policy_data[offset as usize..(offset + METADATA_SIZE) as usize];
290        let metadata = AccessVectorRuleMetadata::read_from_bytes(bytes).unwrap();
291        let permission_data_size = metadata.permission_data_size() as u32;
292        offset + METADATA_SIZE + permission_data_size
293    }
294}
295
296impl HasMetadata for AccessVectorRule {
297    type Metadata = AccessVectorRuleMetadata;
298}
299
300impl Parse for AccessVectorRule {
301    type Error = anyhow::Error;
302
303    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
304        let tail = bytes;
305
306        let (metadata, tail) = PolicyCursor::parse::<AccessVectorRuleMetadata>(tail)?;
307        let access_vector_rule_type = metadata.access_vector_rule_type;
308        let (permission_data, tail) =
309            if (access_vector_rule_type & ACCESS_VECTOR_RULE_DATA_IS_XPERM_MASK) != 0 {
310                let (xperms, tail) = ExtendedPermissions::parse(tail)
311                    .map_err(Into::<anyhow::Error>::into)
312                    .context("parsing extended permissions")?;
313                (PermissionData::ExtendedPermissions(xperms), tail)
314            } else if (access_vector_rule_type & ACCESS_VECTOR_RULE_DATA_IS_TYPE_ID_MASK) != 0 {
315                let (new_type, tail) = PolicyCursor::parse::<le::U32>(tail)?;
316                (PermissionData::NewType(new_type), tail)
317            } else {
318                let (access_vector, tail) = PolicyCursor::parse::<le::U32>(tail)?;
319                (PermissionData::AccessVector(access_vector), tail)
320            };
321        Ok((Self { metadata, permission_data }, tail))
322    }
323}
324
325impl Validate for AccessVectorRule {
326    type Error = anyhow::Error;
327
328    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
329        if self.metadata.class.get() == 0 {
330            return Err(ValidateError::NonOptionalIdIsZero.into());
331        }
332        if let PermissionData::ExtendedPermissions(xperms) = &self.permission_data {
333            let xperms_type = xperms.xperms_type;
334            if !(xperms_type == XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES
335                || xperms_type == XPERMS_TYPE_IOCTL_PREFIXES
336                || xperms_type == XPERMS_TYPE_NLMSG)
337            {
338                return Err(
339                    ValidateError::InvalidExtendedPermissionsType { type_: xperms_type }.into()
340                );
341            }
342        }
343        Ok(())
344    }
345}
346
347#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, Eq, PartialEq, Unaligned, Hash)]
348#[repr(C, packed)]
349pub(super) struct AccessVectorRuleMetadata {
350    source_type: le::U16,
351    target_type: le::U16,
352    class: le::U16,
353    access_vector_rule_type: le::U16,
354}
355
356impl AccessVectorRuleMetadata {
357    pub fn for_query(source: TypeId, target: TypeId, class: ClassId, rule_type: u16) -> Self {
358        let source_type = le::U16::new(source.0.get() as u16);
359        let target_type = le::U16::new(target.0.get() as u16);
360        let class = le::U16::new(class.0.get() as u16);
361        let access_vector_rule_type = le::U16::new(rule_type);
362        Self { source_type, target_type, class, access_vector_rule_type }
363    }
364
365    fn permission_data_size(&self) -> usize {
366        if (self.access_vector_rule_type & ACCESS_VECTOR_RULE_DATA_IS_XPERM_MASK) != 0 {
367            std::mem::size_of::<ExtendedPermissions>()
368        } else if (self.access_vector_rule_type & ACCESS_VECTOR_RULE_DATA_IS_TYPE_ID_MASK) != 0 {
369            std::mem::size_of::<le::U32>()
370        } else {
371            std::mem::size_of::<le::U32>()
372        }
373    }
374}
375
376#[derive(Debug, PartialEq)]
377pub(super) enum PermissionData {
378    AccessVector(le::U32),
379    NewType(le::U32),
380    ExtendedPermissions(ExtendedPermissions),
381}
382
383#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
384#[repr(C, packed)]
385pub(super) struct ExtendedPermissions {
386    pub(super) xperms_type: u8,
387    // xperms_optional_prefix is meaningful when xperms_type is
388    // XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES or XPERMS_TYPE_NLMSG and
389    // meaningless when xperms_type is XPERMS_TYPE_IOCTL_PREFIXES.
390    pub(super) xperms_optional_prefix: u8,
391    pub(super) xperms_bitmap: XpermsBitmap,
392}
393
394impl ExtendedPermissions {
395    #[cfg(test)]
396    fn count(&self) -> u64 {
397        let count = self
398            .xperms_bitmap
399            .0
400            .iter()
401            .fold(0, |count, block| (count as u64) + (block.get().count_ones() as u64));
402        match self.xperms_type {
403            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES | XPERMS_TYPE_NLMSG => count,
404            XPERMS_TYPE_IOCTL_PREFIXES => count * 0x100,
405            _ => unreachable!("invalid xperms_type in validated ExtendedPermissions"),
406        }
407    }
408
409    #[cfg(test)]
410    fn contains(&self, xperm: u16) -> bool {
411        let [postfix, prefix] = xperm.to_le_bytes();
412        if (self.xperms_type == XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES
413            || self.xperms_type == XPERMS_TYPE_NLMSG)
414            && self.xperms_optional_prefix != prefix
415        {
416            return false;
417        }
418        let value = match self.xperms_type {
419            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES | XPERMS_TYPE_NLMSG => postfix,
420            XPERMS_TYPE_IOCTL_PREFIXES => prefix,
421            _ => unreachable!("invalid xperms_type in validated ExtendedPermissions"),
422        };
423        self.xperms_bitmap.contains(value)
424    }
425}
426
427// A bitmap representing a subset of `{0x0,...,0xff}`.
428#[derive(Clone, Copy, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
429#[repr(C, packed)]
430pub struct XpermsBitmap([le::U32; 8]);
431
432impl XpermsBitmap {
433    const BITMAP_BLOCKS: usize = 8;
434    pub const ALL: Self = Self([le::U32::MAX_VALUE; Self::BITMAP_BLOCKS]);
435    pub const NONE: Self = Self([le::U32::ZERO; Self::BITMAP_BLOCKS]);
436
437    #[cfg(test)]
438    pub fn new(elements: [le::U32; 8]) -> Self {
439        Self(elements)
440    }
441
442    pub fn contains(&self, value: u8) -> bool {
443        let block_index = (value as usize) / 32;
444        let bit_index = ((value as usize) % 32) as u32;
445        self.0[block_index] & le::U32::new(1).shl(bit_index) != 0
446    }
447}
448
449/// The xperms cache uses a u64-based representation.
450impl From<[u64; 4]> for XpermsBitmap {
451    fn from(v: [u64; 4]) -> Self {
452        let mut elements = [le::U32::ZERO; 8];
453        for (i, &val) in v.iter().enumerate() {
454            elements[i * 2] = le::U32::new(val as u32);
455            elements[i * 2 + 1] = le::U32::new((val >> 32) as u32);
456        }
457        XpermsBitmap(elements)
458    }
459}
460
461impl From<XpermsBitmap> for [u64; 4] {
462    fn from(v: XpermsBitmap) -> Self {
463        let mut result = [0u64; 4];
464        for i in 0..4 {
465            let low = v.0[i * 2].get() as u64;
466            let high = v.0[i * 2 + 1].get() as u64;
467            result[i] = low | (high << 32);
468        }
469        result
470    }
471}
472
473impl std::ops::BitAnd for XpermsBitmap {
474    type Output = Self;
475    fn bitand(self, rhs: Self) -> Self {
476        let mut result = self;
477        (0..Self::BITMAP_BLOCKS).for_each(|i| result.0[i] &= rhs.0[i]);
478        result
479    }
480}
481
482impl std::ops::BitOr for XpermsBitmap {
483    type Output = Self;
484    fn bitor(self, rhs: Self) -> Self {
485        let mut result = self;
486        (0..Self::BITMAP_BLOCKS).for_each(|i| result.0[i] |= rhs.0[i]);
487        result
488    }
489}
490
491impl std::ops::Not for XpermsBitmap {
492    type Output = Self;
493    fn not(self) -> Self {
494        let mut result = self;
495        (0..Self::BITMAP_BLOCKS).for_each(|i| result.0[i] = !result.0[i]);
496        result
497    }
498}
499
500impl std::ops::BitOrAssign<&Self> for XpermsBitmap {
501    fn bitor_assign(&mut self, rhs: &Self) {
502        (0..Self::BITMAP_BLOCKS).for_each(|i| self.0[i] |= rhs.0[i])
503    }
504}
505
506impl std::ops::SubAssign<&Self> for XpermsBitmap {
507    fn sub_assign(&mut self, rhs: &Self) {
508        (0..Self::BITMAP_BLOCKS).for_each(|i| self.0[i] = self.0[i] ^ (self.0[i] & rhs.0[i]))
509    }
510}
511
512array_type!(RoleTransitions, le::U32, RoleTransition);
513
514array_type_validate_deref_both!(RoleTransitions);
515
516impl ValidateArray<le::U32, RoleTransition> for RoleTransitions {
517    type Error = anyhow::Error;
518
519    /// [`RoleTransitions`] have no additional metadata (beyond length encoding).
520    fn validate_array(
521        _context: &PolicyValidationContext,
522        _metadata: &le::U32,
523        _items: &[RoleTransition],
524    ) -> Result<(), Self::Error> {
525        Ok(())
526    }
527}
528
529#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
530#[repr(C, packed)]
531pub(super) struct RoleTransition {
532    role: le::U32,
533    role_type: le::U32,
534    new_role: le::U32,
535    tclass: le::U32,
536}
537
538impl RoleTransition {
539    pub(super) fn current_role(&self) -> RoleId {
540        RoleId(NonZeroU32::new(self.role.get()).unwrap())
541    }
542
543    pub(super) fn type_(&self) -> TypeId {
544        TypeId(NonZeroU32::new(self.role_type.get()).unwrap())
545    }
546
547    pub(super) fn class(&self) -> ClassId {
548        ClassId(NonZeroU32::new(self.tclass.get()).unwrap())
549    }
550
551    pub(super) fn new_role(&self) -> RoleId {
552        RoleId(NonZeroU32::new(self.new_role.get()).unwrap())
553    }
554}
555
556impl Validate for RoleTransition {
557    type Error = anyhow::Error;
558
559    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
560        NonZeroU32::new(self.role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
561        NonZeroU32::new(self.role_type.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
562        NonZeroU32::new(self.tclass.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
563        NonZeroU32::new(self.new_role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
564        Ok(())
565    }
566}
567
568array_type!(RoleAllows, le::U32, RoleAllow);
569
570array_type_validate_deref_both!(RoleAllows);
571
572impl ValidateArray<le::U32, RoleAllow> for RoleAllows {
573    type Error = anyhow::Error;
574
575    /// [`RoleAllows`] have no additional metadata (beyond length encoding).
576    fn validate_array(
577        _context: &PolicyValidationContext,
578        _metadata: &le::U32,
579        _items: &[RoleAllow],
580    ) -> Result<(), Self::Error> {
581        Ok(())
582    }
583}
584
585#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
586#[repr(C, packed)]
587pub(super) struct RoleAllow {
588    role: le::U32,
589    new_role: le::U32,
590}
591
592impl RoleAllow {
593    pub(super) fn source_role(&self) -> RoleId {
594        RoleId(NonZeroU32::new(self.role.get()).unwrap())
595    }
596
597    pub(super) fn new_role(&self) -> RoleId {
598        RoleId(NonZeroU32::new(self.new_role.get()).unwrap())
599    }
600}
601
602impl Validate for RoleAllow {
603    type Error = anyhow::Error;
604
605    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
606        NonZeroU32::new(self.role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
607        NonZeroU32::new(self.new_role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
608        Ok(())
609    }
610}
611
612#[derive(Debug, PartialEq)]
613pub(super) enum FilenameTransitionList {
614    PolicyVersionGeq33(SimpleArray<FilenameTransition>),
615    PolicyVersionLeq32(SimpleArray<DeprecatedFilenameTransition>),
616}
617
618impl Validate for FilenameTransitionList {
619    type Error = anyhow::Error;
620
621    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
622        match self {
623            Self::PolicyVersionLeq32(list) => {
624                list.validate(context).map_err(Into::<anyhow::Error>::into)
625            }
626            Self::PolicyVersionGeq33(list) => {
627                list.validate(context).map_err(Into::<anyhow::Error>::into)
628            }
629        }
630    }
631}
632
633impl Validate for FilenameTransition {
634    type Error = anyhow::Error;
635    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
636        Ok(())
637    }
638}
639
640#[derive(Debug, PartialEq)]
641pub(super) struct FilenameTransition {
642    filename: SimpleArray<u8>,
643    transition_type: le::U32,
644    transition_class: le::U32,
645    items: SimpleArray<FilenameTransitionItem>,
646}
647
648impl FilenameTransition {
649    pub(super) fn name_bytes(&self) -> &[u8] {
650        &self.filename.data
651    }
652
653    pub(super) fn target_type(&self) -> TypeId {
654        TypeId(NonZeroU32::new(self.transition_type.get()).unwrap())
655    }
656
657    pub(super) fn target_class(&self) -> ClassId {
658        ClassId(NonZeroU32::new(self.transition_class.get()).unwrap())
659    }
660
661    pub(super) fn outputs(&self) -> &[FilenameTransitionItem] {
662        &self.items.data
663    }
664}
665
666impl Parse for FilenameTransition
667where
668    SimpleArray<u8>: Parse,
669    SimpleArray<FilenameTransitionItem>: Parse,
670{
671    type Error = anyhow::Error;
672
673    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
674        let tail = bytes;
675
676        let (filename, tail) = SimpleArray::<u8>::parse(tail)
677            .map_err(Into::<anyhow::Error>::into)
678            .context("parsing filename for filename transition")?;
679
680        let (transition_type, tail) = PolicyCursor::parse::<le::U32>(tail)?;
681
682        let (transition_class, tail) = PolicyCursor::parse::<le::U32>(tail)?;
683
684        let (items, tail) = SimpleArray::<FilenameTransitionItem>::parse(tail)
685            .map_err(Into::<anyhow::Error>::into)
686            .context("parsing items for filename transition")?;
687
688        Ok((Self { filename, transition_type, transition_class, items }, tail))
689    }
690}
691
692#[derive(Debug, PartialEq)]
693pub(super) struct FilenameTransitionItem {
694    stypes: ExtensibleBitmap,
695    out_type: le::U32,
696}
697
698impl FilenameTransitionItem {
699    pub(super) fn has_source_type(&self, source_type: TypeId) -> bool {
700        self.stypes.is_set(source_type.0.get() - 1)
701    }
702
703    pub(super) fn out_type(&self) -> TypeId {
704        TypeId(NonZeroU32::new(self.out_type.get()).unwrap())
705    }
706}
707
708impl Parse for FilenameTransitionItem
709where
710    ExtensibleBitmap: Parse,
711{
712    type Error = anyhow::Error;
713
714    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
715        let tail = bytes;
716
717        let (stypes, tail) = ExtensibleBitmap::parse(tail)
718            .map_err(Into::<anyhow::Error>::into)
719            .context("parsing stypes extensible bitmap for file transition")?;
720
721        let (out_type, tail) = PolicyCursor::parse::<le::U32>(tail)?;
722
723        Ok((Self { stypes, out_type }, tail))
724    }
725}
726
727impl Validate for DeprecatedFilenameTransition {
728    type Error = anyhow::Error;
729    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
730        Ok(())
731    }
732}
733
734#[derive(Debug, PartialEq)]
735pub(super) struct DeprecatedFilenameTransition {
736    filename: SimpleArray<u8>,
737    metadata: DeprecatedFilenameTransitionMetadata,
738}
739
740impl DeprecatedFilenameTransition {
741    pub(super) fn name_bytes(&self) -> &[u8] {
742        &self.filename.data
743    }
744
745    pub(super) fn source_type(&self) -> TypeId {
746        TypeId(NonZeroU32::new(self.metadata.source_type.get()).unwrap())
747    }
748
749    pub(super) fn target_type(&self) -> TypeId {
750        TypeId(NonZeroU32::new(self.metadata.transition_type.get()).unwrap())
751    }
752
753    pub(super) fn target_class(&self) -> ClassId {
754        ClassId(NonZeroU32::new(self.metadata.transition_class.get()).unwrap())
755    }
756
757    pub(super) fn out_type(&self) -> TypeId {
758        TypeId(NonZeroU32::new(self.metadata.out_type.get()).unwrap())
759    }
760}
761
762impl Parse for DeprecatedFilenameTransition
763where
764    SimpleArray<u8>: Parse,
765{
766    type Error = anyhow::Error;
767
768    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
769        let tail = bytes;
770
771        let (filename, tail) = SimpleArray::<u8>::parse(tail)
772            .map_err(Into::<anyhow::Error>::into)
773            .context("parsing filename for deprecated filename transition")?;
774
775        let (metadata, tail) = PolicyCursor::parse::<DeprecatedFilenameTransitionMetadata>(tail)?;
776
777        Ok((Self { filename, metadata }, tail))
778    }
779}
780
781#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
782#[repr(C, packed)]
783pub(super) struct DeprecatedFilenameTransitionMetadata {
784    source_type: le::U32,
785    transition_type: le::U32,
786    transition_class: le::U32,
787    out_type: le::U32,
788}
789
790impl Validate for SimpleArray<InitialSid> {
791    type Error = anyhow::Error;
792
793    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
794        for initial_sid in crate::InitialSid::all_variants() {
795            if *initial_sid == crate::InitialSid::Init && !context.need_init_sid {
796                continue;
797            }
798            self.data
799                .iter()
800                .find(|initial| initial.id().get() == *initial_sid as u32)
801                .ok_or(ValidateError::MissingInitialSid { initial_sid: *initial_sid })?;
802        }
803        Ok(())
804    }
805}
806
807#[derive(Debug, PartialEq)]
808pub(super) struct InitialSid {
809    id: le::U32,
810    context: Context,
811}
812
813impl InitialSid {
814    pub(super) fn id(&self) -> le::U32 {
815        self.id
816    }
817
818    pub(super) fn context(&self) -> &Context {
819        &self.context
820    }
821}
822
823impl Parse for InitialSid
824where
825    Context: Parse,
826{
827    type Error = anyhow::Error;
828
829    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
830        let tail = bytes;
831
832        let (id, tail) = PolicyCursor::parse::<le::U32>(tail)?;
833
834        let (context, tail) = Context::parse(tail)
835            .map_err(Into::<anyhow::Error>::into)
836            .context("parsing context for initial sid")?;
837
838        Ok((Self { id, context }, tail))
839    }
840}
841
842#[derive(Debug, PartialEq)]
843pub(super) struct Context {
844    metadata: ContextMetadata,
845    mls_range: MlsRange,
846}
847
848impl Context {
849    pub(super) fn user_id(&self) -> UserId {
850        UserId(NonZeroU32::new(self.metadata.user.get()).unwrap())
851    }
852    pub(super) fn role_id(&self) -> RoleId {
853        RoleId(NonZeroU32::new(self.metadata.role.get()).unwrap())
854    }
855    pub(super) fn type_id(&self) -> TypeId {
856        TypeId(NonZeroU32::new(self.metadata.context_type.get()).unwrap())
857    }
858    pub(super) fn low_level(&self) -> &MlsLevel {
859        self.mls_range.low()
860    }
861    pub(super) fn high_level(&self) -> &Option<MlsLevel> {
862        self.mls_range.high()
863    }
864}
865
866impl Parse for Context
867where
868    MlsRange: Parse,
869{
870    type Error = anyhow::Error;
871
872    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
873        let tail = bytes;
874
875        let (metadata, tail) =
876            PolicyCursor::parse::<ContextMetadata>(tail).context("parsing metadata for context")?;
877
878        let (mls_range, tail) = MlsRange::parse(tail)
879            .map_err(Into::<anyhow::Error>::into)
880            .context("parsing mls range for context")?;
881
882        Ok((Self { metadata, mls_range }, tail))
883    }
884}
885
886#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
887#[repr(C, packed)]
888pub(super) struct ContextMetadata {
889    user: le::U32,
890    role: le::U32,
891    context_type: le::U32,
892}
893
894impl Validate for NamedContextPair {
895    type Error = anyhow::Error;
896
897    /// TODO: Validate consistency of sequence of [`NamedContextPairs`] objects.
898    ///
899    /// TODO: Is different validation required for `filesystems` and `network_interfaces`? If so,
900    /// create wrapper types with different [`Validate`] implementations.
901    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
902        Ok(())
903    }
904}
905
906#[derive(Debug, PartialEq)]
907pub(super) struct NamedContextPair {
908    name: SimpleArray<u8>,
909    context1: Context,
910    context2: Context,
911}
912
913impl Parse for NamedContextPair
914where
915    SimpleArray<u8>: Parse,
916    Context: Parse,
917{
918    type Error = anyhow::Error;
919
920    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
921        let tail = bytes;
922
923        let (name, tail) = SimpleArray::parse(tail)
924            .map_err(Into::<anyhow::Error>::into)
925            .context("parsing filesystem context name")?;
926
927        let (context1, tail) = Context::parse(tail)
928            .map_err(Into::<anyhow::Error>::into)
929            .context("parsing first context for filesystem context")?;
930
931        let (context2, tail) = Context::parse(tail)
932            .map_err(Into::<anyhow::Error>::into)
933            .context("parsing second context for filesystem context")?;
934
935        Ok((Self { name, context1, context2 }, tail))
936    }
937}
938
939impl Validate for Port {
940    type Error = anyhow::Error;
941
942    /// TODO: Validate consistency of sequence of [`Ports`] objects.
943    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
944        Ok(())
945    }
946}
947
948#[derive(Debug, PartialEq)]
949pub(super) struct Port {
950    metadata: PortMetadata,
951    context: Context,
952}
953
954impl Parse for Port
955where
956    Context: Parse,
957{
958    type Error = anyhow::Error;
959
960    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
961        let tail = bytes;
962
963        let (metadata, tail) =
964            PolicyCursor::parse::<PortMetadata>(tail).context("parsing metadata for context")?;
965
966        let (context, tail) = Context::parse(tail)
967            .map_err(Into::<anyhow::Error>::into)
968            .context("parsing context for port")?;
969
970        Ok((Self { metadata, context }, tail))
971    }
972}
973
974#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
975#[repr(C, packed)]
976pub(super) struct PortMetadata {
977    protocol: le::U32,
978    low_port: le::U32,
979    high_port: le::U32,
980}
981
982impl Validate for Node {
983    type Error = anyhow::Error;
984
985    /// TODO: Validate consistency of sequence of [`Node`] objects.
986    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
987        Ok(())
988    }
989}
990
991#[derive(Debug, PartialEq)]
992pub(super) struct Node {
993    address: le::U32,
994    mask: le::U32,
995    context: Context,
996}
997
998impl Parse for Node
999where
1000    Context: Parse,
1001{
1002    type Error = anyhow::Error;
1003
1004    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1005        let tail = bytes;
1006
1007        let (address, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1008
1009        let (mask, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1010
1011        let (context, tail) = Context::parse(tail)
1012            .map_err(Into::<anyhow::Error>::into)
1013            .context("parsing context for node")?;
1014
1015        Ok((Self { address, mask, context }, tail))
1016    }
1017}
1018
1019#[derive(Debug, PartialEq)]
1020pub(super) struct FsUse {
1021    behavior_and_name: Array<FsUseMetadata, u8>,
1022    context: Context,
1023}
1024
1025impl FsUse {
1026    pub fn fs_type(&self) -> &[u8] {
1027        &self.behavior_and_name.data
1028    }
1029
1030    pub(super) fn behavior(&self) -> FsUseType {
1031        FsUseType::try_from(self.behavior_and_name.metadata.behavior).unwrap()
1032    }
1033
1034    pub(super) fn context(&self) -> &Context {
1035        &self.context
1036    }
1037}
1038
1039impl Parse for FsUse
1040where
1041    Array<FsUseMetadata, u8>: Parse,
1042    Context: Parse,
1043{
1044    type Error = anyhow::Error;
1045
1046    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1047        let tail = bytes;
1048
1049        let (behavior_and_name, tail) = Array::<FsUseMetadata, u8>::parse(tail)
1050            .map_err(Into::<anyhow::Error>::into)
1051            .context("parsing fs use metadata")?;
1052
1053        let (context, tail) = Context::parse(tail)
1054            .map_err(Into::<anyhow::Error>::into)
1055            .context("parsing context for fs use")?;
1056
1057        Ok((Self { behavior_and_name, context }, tail))
1058    }
1059}
1060
1061impl Validate for FsUse {
1062    type Error = anyhow::Error;
1063
1064    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1065        FsUseType::try_from(self.behavior_and_name.metadata.behavior)?;
1066
1067        Ok(())
1068    }
1069}
1070
1071#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1072#[repr(C, packed)]
1073pub(super) struct FsUseMetadata {
1074    /// The type of `fs_use` statement.
1075    behavior: le::U32,
1076    /// The length of the name in the name_and_behavior field of FsUse.
1077    name_length: le::U32,
1078}
1079
1080impl Counted for FsUseMetadata {
1081    fn count(&self) -> u32 {
1082        self.name_length.get()
1083    }
1084}
1085
1086/// Discriminates among the different kinds of "fs_use_*" labeling statements in the policy; see
1087/// https://selinuxproject.org/page/FileStatements.
1088#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
1089pub enum FsUseType {
1090    Xattr = 1,
1091    Trans = 2,
1092    Task = 3,
1093}
1094
1095impl TryFrom<le::U32> for FsUseType {
1096    type Error = anyhow::Error;
1097
1098    fn try_from(value: le::U32) -> Result<Self, Self::Error> {
1099        match value.get() {
1100            1 => Ok(FsUseType::Xattr),
1101            2 => Ok(FsUseType::Trans),
1102            3 => Ok(FsUseType::Task),
1103            _ => Err(ValidateError::InvalidFsUseType { value: value.get() }.into()),
1104        }
1105    }
1106}
1107
1108impl Validate for IPv6Node {
1109    type Error = anyhow::Error;
1110
1111    /// TODO: Validate consistency of sequence of [`IPv6Node`] objects.
1112    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1113        Ok(())
1114    }
1115}
1116
1117#[derive(Debug, PartialEq)]
1118pub(super) struct IPv6Node {
1119    address: [le::U32; 4],
1120    mask: [le::U32; 4],
1121    context: Context,
1122}
1123
1124impl Parse for IPv6Node
1125where
1126    Context: Parse,
1127{
1128    type Error = anyhow::Error;
1129
1130    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1131        let tail = bytes;
1132
1133        let (address, tail) = PolicyCursor::parse::<[le::U32; 4]>(tail)?;
1134
1135        let (mask, tail) = PolicyCursor::parse::<[le::U32; 4]>(tail)?;
1136
1137        let (context, tail) = Context::parse(tail)
1138            .map_err(Into::<anyhow::Error>::into)
1139            .context("parsing context for ipv6 node")?;
1140
1141        Ok((Self { address, mask, context }, tail))
1142    }
1143}
1144
1145impl Validate for InfinitiBandPartitionKey {
1146    type Error = anyhow::Error;
1147
1148    /// TODO: Validate consistency of sequence of [`InfinitiBandPartitionKey`] objects.
1149    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1150        Ok(())
1151    }
1152}
1153
1154#[derive(Debug, PartialEq)]
1155pub(super) struct InfinitiBandPartitionKey {
1156    low: le::U32,
1157    high: le::U32,
1158    context: Context,
1159}
1160
1161impl Parse for InfinitiBandPartitionKey
1162where
1163    Context: Parse,
1164{
1165    type Error = anyhow::Error;
1166
1167    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1168        let tail = bytes;
1169
1170        let (low, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1171
1172        let (high, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1173
1174        let (context, tail) = Context::parse(tail)
1175            .map_err(Into::<anyhow::Error>::into)
1176            .context("parsing context for infiniti band partition key")?;
1177
1178        Ok((Self { low, high, context }, tail))
1179    }
1180}
1181
1182impl Validate for InfinitiBandEndPort {
1183    type Error = anyhow::Error;
1184
1185    /// TODO: Validate sequence of [`InfinitiBandEndPort`] objects.
1186    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1187        Ok(())
1188    }
1189}
1190
1191#[derive(Debug, PartialEq)]
1192pub(super) struct InfinitiBandEndPort {
1193    port_and_name: Array<InfinitiBandEndPortMetadata, u8>,
1194    context: Context,
1195}
1196
1197impl Parse for InfinitiBandEndPort
1198where
1199    Array<InfinitiBandEndPortMetadata, u8>: Parse,
1200    Context: Parse,
1201{
1202    type Error = anyhow::Error;
1203
1204    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1205        let tail = bytes;
1206
1207        let (port_and_name, tail) = Array::<InfinitiBandEndPortMetadata, u8>::parse(tail)
1208            .map_err(Into::<anyhow::Error>::into)
1209            .context("parsing infiniti band end port metadata")?;
1210
1211        let (context, tail) = Context::parse(tail)
1212            .map_err(Into::<anyhow::Error>::into)
1213            .context("parsing context for infiniti band end port")?;
1214
1215        Ok((Self { port_and_name, context }, tail))
1216    }
1217}
1218
1219#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1220#[repr(C, packed)]
1221pub(super) struct InfinitiBandEndPortMetadata {
1222    length: le::U32,
1223    port: le::U32,
1224}
1225
1226impl Counted for InfinitiBandEndPortMetadata {
1227    fn count(&self) -> u32 {
1228        self.length.get()
1229    }
1230}
1231
1232impl Validate for GenericFsContext {
1233    type Error = anyhow::Error;
1234
1235    /// TODO: Validate sequence of  [`GenericFsContext`] objects.
1236    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1237        Ok(())
1238    }
1239}
1240
1241/// Information parsed parsed from `genfscon [fs_type] [partial_path] [fs_context]` statements
1242/// about a specific filesystem type.
1243#[derive(Debug)]
1244pub(super) struct GenericFsContext {
1245    fs_type: SimpleArray<u8>,
1246    fs_context: SimpleArrayView<FsContext>,
1247}
1248
1249impl GenericFsContext {
1250    /// Returns the `fs_type` representation to be used when looking up in a CustomKeyHashedView.
1251    pub(super) fn for_query(fs_type: &str) -> SimpleArray<u8> {
1252        Array { data: fs_type.as_bytes().to_vec(), metadata: le::U32::new(fs_type.len() as u32) }
1253    }
1254}
1255
1256impl Parse for GenericFsContext {
1257    type Error = anyhow::Error;
1258
1259    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1260        let tail = bytes;
1261
1262        let (fs_type, tail) = SimpleArray::<u8>::parse(tail)
1263            .map_err(Into::<anyhow::Error>::into)
1264            .context("parsing fs_type for generic fs context")?;
1265
1266        let (fs_context, tail) = SimpleArrayView::<FsContext>::parse(tail)
1267            .map_err(Into::<anyhow::Error>::into)
1268            .context("parsing fs_context for generic fs context")?;
1269
1270        Ok((Self { fs_type, fs_context }, tail))
1271    }
1272}
1273
1274impl Hashable for GenericFsContext {
1275    type Key = SimpleArray<u8>;
1276    type Value = FsContext;
1277
1278    fn key(&self) -> &Self::Key {
1279        &self.fs_type
1280    }
1281
1282    fn values(&self) -> &SimpleArrayView<Self::Value> {
1283        &self.fs_context
1284    }
1285}
1286
1287impl Eq for SimpleArray<u8> {}
1288
1289impl Hash for SimpleArray<u8> {
1290    fn hash<H: Hasher>(&self, state: &mut H) {
1291        self.data.hash(state);
1292    }
1293}
1294
1295impl SimpleArrayView<FsContext> {
1296    fn try_validate_alphabetic_order(&self, context: &PolicyValidationContext) -> bool {
1297        self.data()
1298            .iter(&context.data)
1299            .map(|view| view.parse(&context.data).partial_path().to_vec())
1300            .is_sorted_by(|a, b| a <= b)
1301    }
1302
1303    fn try_validate_length_descending_order(&self, context: &PolicyValidationContext) -> bool {
1304        self.data()
1305            .iter(&context.data)
1306            .map(|view| view.parse(&context.data).partial_path().len())
1307            .is_sorted_by(|a, b| a >= b)
1308    }
1309}
1310
1311impl Validate for SimpleArrayView<FsContext> {
1312    type Error = anyhow::Error;
1313
1314    /// Checks that the sequence of [`FsContext`] objects is valid.
1315    /// To be valid, FsContexts must be sorted by either:
1316    /// - the length of sub-paths (descending order).
1317    /// - alphabetically by sub-paths (ascending order).
1318    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
1319        if !self.try_validate_alphabetic_order(context)
1320            && !self.try_validate_length_descending_order(context)
1321        {
1322            return Err(anyhow::anyhow!(
1323                "FsContexts must be sorted by partial path length (descending) or alphabetically.",
1324            ));
1325        }
1326        Ok(())
1327    }
1328}
1329
1330#[derive(Debug, PartialEq)]
1331pub(super) struct FsContext {
1332    /// The partial path, relative to the root of the filesystem. The partial path can only be set for
1333    /// virtual filesystems, like `proc/`. Otherwise, this must be `/`
1334    partial_path: SimpleArray<u8>,
1335    /// Optional. When provided, the context will only be applied to files of this type. Allowed files
1336    /// types are: blk_file, chr_file, dir, fifo_file, lnk_file, sock_file, file. When set to 0, the
1337    /// context applies to all file types.
1338    class: le::U32,
1339    /// The security context allocated to the filesystem.
1340    context: Context,
1341}
1342
1343impl FsContext {
1344    pub(super) fn partial_path(&self) -> &[u8] {
1345        &self.partial_path.data
1346    }
1347
1348    pub(super) fn context(&self) -> &Context {
1349        &self.context
1350    }
1351
1352    pub(super) fn class(&self) -> Option<ClassId> {
1353        NonZeroU32::new(self.class.into()).map(ClassId)
1354    }
1355}
1356
1357impl Parse for FsContext
1358where
1359    SimpleArray<u8>: Parse,
1360    Context: Parse,
1361{
1362    type Error = anyhow::Error;
1363
1364    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1365        let tail = bytes;
1366
1367        let (partial_path, tail) = SimpleArray::<u8>::parse(tail)
1368            .map_err(Into::<anyhow::Error>::into)
1369            .context("parsing filesystem context partial path")?;
1370
1371        let (class, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1372
1373        let (context, tail) = Context::parse(tail)
1374            .map_err(Into::<anyhow::Error>::into)
1375            .context("parsing context for filesystem context")?;
1376
1377        Ok((Self { partial_path, class, context }, tail))
1378    }
1379}
1380
1381impl Walk for FsContext {
1382    fn walk(policy_data: &PolicyData, offset: PolicyOffset) -> PolicyOffset {
1383        let cursor = PolicyCursor::new_at(policy_data, offset);
1384        let (_, tail) = FsContext::parse(cursor)
1385            .map_err(Into::<anyhow::Error>::into)
1386            .expect("policy should be valid");
1387        tail.offset()
1388    }
1389}
1390
1391impl Validate for RangeTransition {
1392    type Error = anyhow::Error;
1393    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1394        if self.metadata.target_class.get() == 0 {
1395            return Err(ValidateError::NonOptionalIdIsZero.into());
1396        }
1397        Ok(())
1398    }
1399}
1400
1401#[derive(Debug, PartialEq)]
1402pub(super) struct RangeTransition {
1403    metadata: RangeTransitionMetadata,
1404    mls_range: MlsRange,
1405}
1406
1407impl RangeTransition {
1408    pub fn source_type(&self) -> TypeId {
1409        TypeId(NonZeroU32::new(self.metadata.source_type.get()).unwrap())
1410    }
1411
1412    pub fn target_type(&self) -> TypeId {
1413        TypeId(NonZeroU32::new(self.metadata.target_type.get()).unwrap())
1414    }
1415
1416    pub fn target_class(&self) -> ClassId {
1417        ClassId(NonZeroU32::new(self.metadata.target_class.get()).unwrap())
1418    }
1419
1420    pub fn mls_range(&self) -> &MlsRange {
1421        &self.mls_range
1422    }
1423}
1424
1425impl Parse for RangeTransition
1426where
1427    MlsRange: Parse,
1428{
1429    type Error = anyhow::Error;
1430
1431    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1432        let tail = bytes;
1433
1434        let (metadata, tail) = PolicyCursor::parse::<RangeTransitionMetadata>(tail)
1435            .context("parsing range transition metadata")?;
1436
1437        let (mls_range, tail) = MlsRange::parse(tail)
1438            .map_err(Into::<anyhow::Error>::into)
1439            .context("parsing mls range for range transition")?;
1440
1441        Ok((Self { metadata, mls_range }, tail))
1442    }
1443}
1444
1445#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1446#[repr(C, packed)]
1447pub(super) struct RangeTransitionMetadata {
1448    source_type: le::U32,
1449    target_type: le::U32,
1450    target_class: le::U32,
1451}
1452
1453#[cfg(test)]
1454pub(super) mod testing {
1455    use super::AccessVectorRule;
1456    use std::cmp::Ordering;
1457
1458    pub(in super::super) fn access_vector_rule_ordering(
1459        left: &AccessVectorRule,
1460        right: &AccessVectorRule,
1461    ) -> Ordering {
1462        (
1463            left.metadata.source_type,
1464            left.metadata.target_type,
1465            left.metadata.class,
1466            left.metadata.access_vector_rule_type,
1467        )
1468            .cmp(&(
1469                right.metadata.source_type,
1470                right.metadata.target_type,
1471                right.metadata.class,
1472                right.metadata.access_vector_rule_type,
1473            ))
1474    }
1475}
1476
1477#[cfg(test)]
1478mod tests {
1479    use super::super::{ClassId, find_class_by_name, parse_policy_by_value};
1480    use super::{
1481        ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM, ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM,
1482        ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES,
1483        XPERMS_TYPE_IOCTL_PREFIXES, XPERMS_TYPE_NLMSG,
1484    };
1485    use std::num::NonZeroU32;
1486
1487    impl super::AccessVectorRuleMetadata {
1488        /// Returns whether this access vector rule comes from an
1489        /// `allowxperm [source] [target]:[class] [permission] {
1490        /// [extended_permissions] };` policy statement.
1491        pub fn is_allowxperm(&self) -> bool {
1492            (self.access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM) != 0
1493        }
1494
1495        /// Returns whether this access vector rule comes from an
1496        /// `auditallowxperm [source] [target]:[class] [permission] {
1497        /// [extended_permissions] };` policy statement.
1498        pub fn is_auditallowxperm(&self) -> bool {
1499            (self.access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM) != 0
1500        }
1501
1502        /// Returns whether this access vector rule comes from a
1503        /// `dontauditxperm [source] [target]:[class] [permission] {
1504        /// [extended_permissions] };` policy statement.
1505        pub fn is_dontauditxperm(&self) -> bool {
1506            (self.access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM) != 0
1507        }
1508
1509        /// Returns the target class id in this access vector rule. This id
1510        /// corresponds to the [`super::symbols::Class`] `id()` of some class in the
1511        /// same policy. Although the index is returned as a 32-bit value, the field
1512        /// itself is 16-bit
1513        pub fn target_class(&self) -> ClassId {
1514            ClassId(NonZeroU32::new(self.class.into()).unwrap())
1515        }
1516    }
1517
1518    #[test]
1519    fn parse_allowxperm_one_ioctl() {
1520        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1521        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1522        let parsed_policy = &policy.0;
1523        parsed_policy.validate().expect("validate policy");
1524
1525        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_ioctl")
1526            .expect("look up class_one_ioctl")
1527            .id();
1528
1529        let rules: Vec<_> = parsed_policy
1530            .access_vector_rules_for_test()
1531            .filter(|rule| rule.metadata.target_class() == class_id)
1532            .collect();
1533
1534        assert_eq!(rules.len(), 1);
1535        assert!(rules[0].metadata.is_allowxperm());
1536        if let Some(xperms) = rules[0].extended_permissions() {
1537            assert_eq!(xperms.count(), 1);
1538            assert!(xperms.contains(0xabcd));
1539        } else {
1540            panic!("unexpected permission data type")
1541        }
1542    }
1543
1544    // `ioctl` extended permissions that are declared in the same rule, and have the same
1545    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1546    #[test]
1547    fn parse_allowxperm_two_ioctls_same_range() {
1548        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1549        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1550        let parsed_policy = &policy.0;
1551        parsed_policy.validate().expect("validate policy");
1552
1553        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_ioctls_same_range")
1554            .expect("look up class_two_ioctls_same_range")
1555            .id();
1556
1557        let rules: Vec<_> = parsed_policy
1558            .access_vector_rules_for_test()
1559            .filter(|rule| rule.metadata.target_class() == class_id)
1560            .collect();
1561
1562        assert_eq!(rules.len(), 1);
1563        assert!(rules[0].metadata.is_allowxperm());
1564        if let Some(xperms) = rules[0].extended_permissions() {
1565            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1566            assert_eq!(xperms.xperms_optional_prefix, 0x12);
1567            assert_eq!(xperms.count(), 2);
1568            assert!(xperms.contains(0x1234));
1569            assert!(xperms.contains(0x1256));
1570        } else {
1571            panic!("unexpected permission data type")
1572        }
1573    }
1574
1575    // `ioctl` extended permissions that are declared in different rules, but that have the same
1576    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1577    #[test]
1578    fn parse_allowxperm_two_ioctls_same_range_diff_rules() {
1579        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1580        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1581        let parsed_policy = &policy.0;
1582        parsed_policy.validate().expect("validate policy");
1583
1584        let class_id =
1585            find_class_by_name(&parsed_policy.classes(), "class_four_ioctls_same_range_diff_rules")
1586                .expect("look up class_four_ioctls_same_range_diff_rules")
1587                .id();
1588
1589        let rules: Vec<_> = parsed_policy
1590            .access_vector_rules_for_test()
1591            .filter(|rule| rule.metadata.target_class() == class_id)
1592            .collect();
1593
1594        assert_eq!(rules.len(), 1);
1595        assert!(rules[0].metadata.is_allowxperm());
1596        if let Some(xperms) = rules[0].extended_permissions() {
1597            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1598            assert_eq!(xperms.xperms_optional_prefix, 0x30);
1599            assert_eq!(xperms.count(), 4);
1600            assert!(xperms.contains(0x3008));
1601            assert!(xperms.contains(0x3009));
1602            assert!(xperms.contains(0x3011));
1603            assert!(xperms.contains(0x3013));
1604        } else {
1605            panic!("unexpected permission data type")
1606        }
1607    }
1608
1609    // `ioctl` extended permissions that are declared in the same rule, and have different
1610    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1611    #[test]
1612    fn parse_allowxperm_two_ioctls_different_range() {
1613        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1614        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1615        let parsed_policy = &policy.0;
1616        parsed_policy.validate().expect("validate policy");
1617
1618        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_ioctls_diff_range")
1619            .expect("look up class_two_ioctls_diff_range")
1620            .id();
1621
1622        let rules: Vec<_> = parsed_policy
1623            .access_vector_rules_for_test()
1624            .filter(|rule| rule.metadata.target_class() == class_id)
1625            .collect();
1626
1627        assert_eq!(rules.len(), 2);
1628        assert!(rules[0].metadata.is_allowxperm());
1629        if let Some(xperms) = rules[0].extended_permissions() {
1630            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1631            assert_eq!(xperms.xperms_optional_prefix, 0x56);
1632            assert_eq!(xperms.count(), 1);
1633            assert!(xperms.contains(0x5678));
1634        } else {
1635            panic!("unexpected permission data type")
1636        }
1637        assert!(rules[1].metadata.is_allowxperm());
1638        if let Some(xperms) = rules[1].extended_permissions() {
1639            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1640            assert_eq!(xperms.xperms_optional_prefix, 0x12);
1641            assert_eq!(xperms.count(), 1);
1642            assert!(xperms.contains(0x1234));
1643        } else {
1644            panic!("unexpected permission data type")
1645        }
1646    }
1647
1648    // If a set of `ioctl` extended permissions consists of all xperms with a given high byte,
1649    // then it is represented by one `AccessVectorRule`.
1650    #[test]
1651    fn parse_allowxperm_one_driver_range() {
1652        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1653        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1654        let parsed_policy = &policy.0;
1655        parsed_policy.validate().expect("validate policy");
1656
1657        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_driver_range")
1658            .expect("look up class_one_driver_range")
1659            .id();
1660
1661        let rules: Vec<_> = parsed_policy
1662            .access_vector_rules_for_test()
1663            .filter(|rule| rule.metadata.target_class() == class_id)
1664            .collect();
1665
1666        assert_eq!(rules.len(), 1);
1667        assert!(rules[0].metadata.is_allowxperm());
1668        if let Some(xperms) = rules[0].extended_permissions() {
1669            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1670            assert_eq!(xperms.count(), 0x100);
1671            assert!(xperms.contains(0x1000));
1672            assert!(xperms.contains(0x10ab));
1673        } else {
1674            panic!("unexpected permission data type")
1675        }
1676    }
1677
1678    // If a rule grants `ioctl` extended permissions to a wide range that does not fall cleanly on
1679    // divisible-by-256 boundaries, it gets represented in the policy as three `AccessVectorRule`s:
1680    // two for the smaller subranges at the ends and one for the large subrange in the middle.
1681    #[test]
1682    fn parse_allowxperm_most_ioctls() {
1683        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1684        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1685        let parsed_policy = &policy.0;
1686        parsed_policy.validate().expect("validate policy");
1687
1688        let class_id = find_class_by_name(&parsed_policy.classes(), "class_most_ioctls")
1689            .expect("look up class_most_ioctls")
1690            .id();
1691
1692        let rules: Vec<_> = parsed_policy
1693            .access_vector_rules_for_test()
1694            .filter(|rule| rule.metadata.target_class() == class_id)
1695            .collect();
1696
1697        assert_eq!(rules.len(), 3);
1698        assert!(rules[0].metadata.is_allowxperm());
1699        if let Some(xperms) = rules[0].extended_permissions() {
1700            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1701            assert_eq!(xperms.xperms_optional_prefix, 0xff);
1702            assert_eq!(xperms.count(), 0xfe);
1703            for xperm in 0xff00..0xfffd {
1704                assert!(xperms.contains(xperm));
1705            }
1706        } else {
1707            panic!("unexpected permission data type")
1708        }
1709        if let Some(xperms) = rules[1].extended_permissions() {
1710            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1711            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1712            assert_eq!(xperms.count(), 0xfe);
1713            for xperm in 0x0002..0x0100 {
1714                assert!(xperms.contains(xperm));
1715            }
1716        } else {
1717            panic!("unexpected permission data type")
1718        }
1719        if let Some(xperms) = rules[2].extended_permissions() {
1720            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1721            assert_eq!(xperms.count(), 0xfe00);
1722            for xperm in 0x0100..0xff00 {
1723                assert!(xperms.contains(xperm));
1724            }
1725        } else {
1726            panic!("unexpected permission data type")
1727        }
1728    }
1729
1730    // If a rule grants `ioctl` extended permissions to two wide ranges that do not fall cleanly on
1731    // divisible-by-256 boundaries, they get represented in the policy as five `AccessVectorRule`s:
1732    // four for the smaller subranges at the ends and one for the two large subranges.
1733    #[test]
1734    fn parse_allowxperm_most_ioctls_with_hole() {
1735        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1736        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1737        let parsed_policy = &policy.0;
1738        parsed_policy.validate().expect("validate policy");
1739
1740        let class_id = find_class_by_name(&parsed_policy.classes(), "class_most_ioctls_with_hole")
1741            .expect("look up class_most_ioctls_with_hole")
1742            .id();
1743
1744        let rules: Vec<_> = parsed_policy
1745            .access_vector_rules_for_test()
1746            .filter(|rule| rule.metadata.target_class() == class_id)
1747            .collect();
1748
1749        assert_eq!(rules.len(), 5);
1750        assert!(rules[0].metadata.is_allowxperm());
1751        if let Some(xperms) = rules[0].extended_permissions() {
1752            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1753            assert_eq!(xperms.xperms_optional_prefix, 0xff);
1754            assert_eq!(xperms.count(), 0xfe);
1755            for xperm in 0xff00..0xfffd {
1756                assert!(xperms.contains(xperm));
1757            }
1758        } else {
1759            panic!("unexpected permission data type")
1760        }
1761        if let Some(xperms) = rules[1].extended_permissions() {
1762            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1763            assert_eq!(xperms.xperms_optional_prefix, 0x40);
1764            assert_eq!(xperms.count(), 0xfe);
1765            for xperm in 0x4002..0x4100 {
1766                assert!(xperms.contains(xperm));
1767            }
1768        } else {
1769            panic!("unexpected permission data type")
1770        }
1771        assert!(rules[0].metadata.is_allowxperm());
1772        if let Some(xperms) = rules[2].extended_permissions() {
1773            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1774            assert_eq!(xperms.xperms_optional_prefix, 0x2f);
1775            assert_eq!(xperms.count(), 0xfe);
1776            for xperm in 0x2f00..0x2ffd {
1777                assert!(xperms.contains(xperm));
1778            }
1779        } else {
1780            panic!("unexpected permission data type")
1781        }
1782        if let Some(xperms) = rules[3].extended_permissions() {
1783            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1784            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1785            assert_eq!(xperms.count(), 0xfe);
1786            for xperm in 0x0002..0x0100 {
1787                assert!(xperms.contains(xperm));
1788            }
1789        } else {
1790            panic!("unexpected permission data type")
1791        }
1792        if let Some(xperms) = rules[4].extended_permissions() {
1793            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1794            assert_eq!(xperms.count(), 0xec00);
1795            for xperm in 0x0100..0x2f00 {
1796                assert!(xperms.contains(xperm));
1797            }
1798            for xperm in 0x4100..0xff00 {
1799                assert!(xperms.contains(xperm));
1800            }
1801        } else {
1802            panic!("unexpected permission data type")
1803        }
1804    }
1805
1806    // If a set of `ioctl` extended permissions contains all 16-bit xperms, then it is
1807    // then it is represented by one `AccessVectorRule`. (More generally, the representation
1808    // is a single `AccessVectorRule` as long as the set either fully includes or fully
1809    // excludes each 8-bit prefix range.)
1810    #[test]
1811    fn parse_allowxperm_all_ioctls() {
1812        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1813        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1814        let parsed_policy = &policy.0;
1815        parsed_policy.validate().expect("validate policy");
1816
1817        let class_id = find_class_by_name(&parsed_policy.classes(), "class_all_ioctls")
1818            .expect("look up class_all_ioctls")
1819            .id();
1820
1821        let rules: Vec<_> = parsed_policy
1822            .access_vector_rules_for_test()
1823            .filter(|rule| rule.metadata.target_class() == class_id)
1824            .collect();
1825
1826        assert_eq!(rules.len(), 1);
1827        assert!(rules[0].metadata.is_allowxperm());
1828        if let Some(xperms) = rules[0].extended_permissions() {
1829            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1830            assert_eq!(xperms.count(), 0x10000);
1831        } else {
1832            panic!("unexpected permission data type")
1833        }
1834    }
1835
1836    #[test]
1837    fn parse_allowxperm_one_nlmsg() {
1838        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1839        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1840        let parsed_policy = &policy.0;
1841        parsed_policy.validate().expect("validate policy");
1842
1843        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_nlmsg")
1844            .expect("look up class_one_nlmsg")
1845            .id();
1846
1847        let rules: Vec<_> = parsed_policy
1848            .access_vector_rules_for_test()
1849            .filter(|rule| rule.metadata.target_class() == class_id)
1850            .collect();
1851
1852        assert_eq!(rules.len(), 1);
1853        assert!(rules[0].metadata.is_allowxperm());
1854        if let Some(xperms) = rules[0].extended_permissions() {
1855            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1856            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1857            assert_eq!(xperms.count(), 1);
1858            assert!(xperms.contains(0x12));
1859        } else {
1860            panic!("unexpected permission data type")
1861        }
1862    }
1863
1864    // `nlmsg` extended permissions that are declared in the same rule, and have the same
1865    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1866    #[test]
1867    fn parse_allowxperm_two_nlmsg_same_range() {
1868        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1869        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1870        let parsed_policy = &policy.0;
1871        parsed_policy.validate().expect("validate policy");
1872
1873        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_nlmsg_same_range")
1874            .expect("look up class_two_nlmsg_same_range")
1875            .id();
1876
1877        let rules: Vec<_> = parsed_policy
1878            .access_vector_rules_for_test()
1879            .filter(|rule| rule.metadata.target_class() == class_id)
1880            .collect();
1881
1882        assert_eq!(rules.len(), 1);
1883        assert!(rules[0].metadata.is_allowxperm());
1884        if let Some(xperms) = rules[0].extended_permissions() {
1885            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1886            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1887            assert_eq!(xperms.count(), 2);
1888            assert!(xperms.contains(0x12));
1889            assert!(xperms.contains(0x24));
1890        } else {
1891            panic!("unexpected permission data type")
1892        }
1893    }
1894
1895    // `nlmsg` extended permissions that are declared in the same rule, and have different
1896    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1897    #[test]
1898    fn parse_allowxperm_two_nlmsg_different_range() {
1899        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1900        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1901        let parsed_policy = &policy.0;
1902        parsed_policy.validate().expect("validate policy");
1903
1904        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_nlmsg_diff_range")
1905            .expect("look up class_two_nlmsg_diff_range")
1906            .id();
1907
1908        let rules: Vec<_> = parsed_policy
1909            .access_vector_rules_for_test()
1910            .filter(|rule| rule.metadata.target_class() == class_id)
1911            .collect();
1912
1913        assert_eq!(rules.len(), 2);
1914        assert!(rules[0].metadata.is_allowxperm());
1915        if let Some(xperms) = rules[0].extended_permissions() {
1916            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1917            assert_eq!(xperms.xperms_optional_prefix, 0x10);
1918            assert_eq!(xperms.count(), 1);
1919            assert!(xperms.contains(0x1024));
1920        } else {
1921            panic!("unexpected permission data type")
1922        }
1923        assert!(rules[1].metadata.is_allowxperm());
1924        if let Some(xperms) = rules[1].extended_permissions() {
1925            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1926            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1927            assert_eq!(xperms.count(), 1);
1928            assert!(xperms.contains(0x12));
1929        } else {
1930            panic!("unexpected permission data type")
1931        }
1932    }
1933
1934    // The set of `nlmsg` extended permissions with a given high byte is represented by
1935    // a single `AccessVectorRule` in the compiled policy.
1936    #[test]
1937    fn parse_allowxperm_one_nlmsg_range() {
1938        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1939        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1940        let parsed_policy = &policy.0;
1941        parsed_policy.validate().expect("validate policy");
1942
1943        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_nlmsg_range")
1944            .expect("look up class_one_nlmsg_range")
1945            .id();
1946
1947        let rules: Vec<_> = parsed_policy
1948            .access_vector_rules_for_test()
1949            .filter(|rule| rule.metadata.target_class() == class_id)
1950            .collect();
1951
1952        assert_eq!(rules.len(), 1);
1953        assert!(rules[0].metadata.is_allowxperm());
1954        if let Some(xperms) = rules[0].extended_permissions() {
1955            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1956            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1957            assert_eq!(xperms.count(), 0x100);
1958            for i in 0x0..0xff {
1959                assert!(xperms.contains(i), "{i}");
1960            }
1961        } else {
1962            panic!("unexpected permission data type")
1963        }
1964    }
1965
1966    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
1967    // of 2 given prefix bytes is represented by 2 `AccessVectorRule`s in the compiled policy.
1968    //
1969    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
1970    // are not expected to appear in policies.
1971    #[test]
1972    fn parse_allowxperm_two_nlmsg_ranges() {
1973        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1974        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1975        let parsed_policy = &policy.0;
1976        parsed_policy.validate().expect("validate policy");
1977
1978        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_nlmsg_ranges")
1979            .expect("look up class_two_nlmsg_ranges")
1980            .id();
1981
1982        let rules: Vec<_> = parsed_policy
1983            .access_vector_rules_for_test()
1984            .filter(|rule| rule.metadata.target_class() == class_id)
1985            .collect();
1986
1987        assert_eq!(rules.len(), 2);
1988        assert!(rules[0].metadata.is_allowxperm());
1989        if let Some(xperms) = rules[0].extended_permissions() {
1990            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1991            assert_eq!(xperms.xperms_optional_prefix, 0x01);
1992            assert_eq!(xperms.count(), 0x100);
1993            for i in 0x0100..0x01ff {
1994                assert!(xperms.contains(i), "{i}");
1995            }
1996        } else {
1997            panic!("unexpected permission data type")
1998        }
1999        if let Some(xperms) = rules[1].extended_permissions() {
2000            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2001            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2002            assert_eq!(xperms.count(), 0x100);
2003            for i in 0x0..0xff {
2004                assert!(xperms.contains(i), "{i}");
2005            }
2006        } else {
2007            panic!("unexpected permission data type")
2008        }
2009    }
2010
2011    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
2012    // of 3 non-consecutive prefix bytes is represented by 3 `AccessVectorRule`s in the
2013    // compiled policy.
2014    //
2015    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
2016    // are not expected to appear in policies.
2017    #[test]
2018    fn parse_allowxperm_three_separate_nlmsg_ranges() {
2019        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
2020        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2021        let parsed_policy = &policy.0;
2022        parsed_policy.validate().expect("validate policy");
2023
2024        let class_id =
2025            find_class_by_name(&parsed_policy.classes(), "class_three_separate_nlmsg_ranges")
2026                .expect("look up class_three_separate_nlmsg_ranges")
2027                .id();
2028
2029        let rules: Vec<_> = parsed_policy
2030            .access_vector_rules_for_test()
2031            .filter(|rule| rule.metadata.target_class() == class_id)
2032            .collect();
2033
2034        assert_eq!(rules.len(), 3);
2035        assert!(rules[0].metadata.is_allowxperm());
2036        if let Some(xperms) = rules[0].extended_permissions() {
2037            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2038            assert_eq!(xperms.xperms_optional_prefix, 0x20);
2039            assert_eq!(xperms.count(), 0x100);
2040            for i in 0x2000..0x20ff {
2041                assert!(xperms.contains(i), "{i}");
2042            }
2043        } else {
2044            panic!("unexpected permission data type")
2045        }
2046        if let Some(xperms) = rules[1].extended_permissions() {
2047            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2048            assert_eq!(xperms.xperms_optional_prefix, 0x10);
2049            assert_eq!(xperms.count(), 0x100);
2050            for i in 0x1000..0x10ff {
2051                assert!(xperms.contains(i), "{i}");
2052            }
2053        } else {
2054            panic!("unexpected permission data type")
2055        }
2056        if let Some(xperms) = rules[2].extended_permissions() {
2057            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2058            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2059            assert_eq!(xperms.count(), 0x100);
2060            for i in 0x0..0xff {
2061                assert!(xperms.contains(i), "{i}");
2062            }
2063        } else {
2064            panic!("unexpected permission data type")
2065        }
2066    }
2067
2068    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
2069    // of 3 (or more) consecutive prefix bytes is represented by 2 `AccessVectorRule`s in the
2070    // compiled policy, one for the smallest prefix byte and one for the largest.
2071    //
2072    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
2073    // are not expected to appear in policies.
2074    #[test]
2075    fn parse_allowxperm_three_contiguous_nlmsg_ranges() {
2076        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
2077        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2078        let parsed_policy = &policy.0;
2079        parsed_policy.validate().expect("validate policy");
2080
2081        let class_id =
2082            find_class_by_name(&parsed_policy.classes(), "class_three_contiguous_nlmsg_ranges")
2083                .expect("look up class_three_contiguous_nlmsg_ranges")
2084                .id();
2085
2086        let rules: Vec<_> = parsed_policy
2087            .access_vector_rules_for_test()
2088            .filter(|rule| rule.metadata.target_class() == class_id)
2089            .collect();
2090
2091        assert_eq!(rules.len(), 2);
2092        assert!(rules[0].metadata.is_allowxperm());
2093        if let Some(xperms) = rules[0].extended_permissions() {
2094            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2095            assert_eq!(xperms.xperms_optional_prefix, 0x02);
2096            assert_eq!(xperms.count(), 0x100);
2097            for i in 0x0200..0x02ff {
2098                assert!(xperms.contains(i), "{i}");
2099            }
2100        } else {
2101            panic!("unexpected permission data type")
2102        }
2103        if let Some(xperms) = rules[1].extended_permissions() {
2104            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2105            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2106            assert_eq!(xperms.count(), 0x100);
2107            for i in 0x0..0xff {
2108                assert!(xperms.contains(i), "{i}");
2109            }
2110        } else {
2111            panic!("unexpected permission data type")
2112        }
2113    }
2114
2115    // The representation of extended permissions for `auditallowxperm` rules is
2116    // the same as for `allowxperm` rules.
2117    #[test]
2118    fn parse_auditallowxperm() {
2119        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
2120        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2121        let parsed_policy = &policy.0;
2122        parsed_policy.validate().expect("validate policy");
2123
2124        let class_id = find_class_by_name(&parsed_policy.classes(), "class_auditallowxperm")
2125            .expect("look up class_auditallowxperm")
2126            .id();
2127
2128        let rules: Vec<_> = parsed_policy
2129            .access_vector_rules_for_test()
2130            .filter(|rule| rule.metadata.target_class() == class_id)
2131            .collect();
2132
2133        assert_eq!(rules.len(), 2);
2134        assert!(rules[0].metadata.is_auditallowxperm());
2135        if let Some(xperms) = rules[0].extended_permissions() {
2136            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2137            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2138            assert_eq!(xperms.count(), 1);
2139            assert!(xperms.contains(0x10));
2140        } else {
2141            panic!("unexpected permission data type")
2142        }
2143        if let Some(xperms) = rules[1].extended_permissions() {
2144            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
2145            assert_eq!(xperms.xperms_optional_prefix, 0x10);
2146            assert_eq!(xperms.count(), 1);
2147            assert!(xperms.contains(0x1000));
2148        } else {
2149            panic!("unexpected permission data type")
2150        }
2151    }
2152
2153    // The representation of extended permissions for `dontauditxperm` rules is
2154    // the same as for `allowxperm` rules. In particular, the `AccessVectorRule`
2155    // contains the same set of extended permissions that appears in the text
2156    // policy. (This differs from the representation of the access vector in
2157    // `AccessVectorRule`s for `dontaudit` rules, where the `AccessVectorRule`
2158    // contains the complement of the access vector that appears in the text
2159    // policy.)
2160    #[test]
2161    fn parse_dontauditxperm() {
2162        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
2163        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2164        let parsed_policy = &policy.0;
2165        parsed_policy.validate().expect("validate policy");
2166
2167        let class_id = find_class_by_name(&parsed_policy.classes(), "class_dontauditxperm")
2168            .expect("look up class_dontauditxperm")
2169            .id();
2170
2171        let rules: Vec<_> = parsed_policy
2172            .access_vector_rules_for_test()
2173            .filter(|rule| rule.metadata.target_class() == class_id)
2174            .collect();
2175
2176        assert_eq!(rules.len(), 2);
2177        assert!(rules[0].metadata.is_dontauditxperm());
2178        if let Some(xperms) = rules[0].extended_permissions() {
2179            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2180            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2181            assert_eq!(xperms.count(), 1);
2182            assert!(xperms.contains(0x11));
2183        } else {
2184            panic!("unexpected permission data type")
2185        }
2186        if let Some(xperms) = rules[1].extended_permissions() {
2187            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
2188            assert_eq!(xperms.xperms_optional_prefix, 0x10);
2189            assert_eq!(xperms.count(), 1);
2190            assert!(xperms.contains(0x1000));
2191        } else {
2192            panic!("unexpected permission data type")
2193        }
2194    }
2195
2196    // If an allowxperm rule and an auditallowxperm rule specify exactly the same permissions, they
2197    // are not coalesced into a single `AccessVectorRule` in the policy; two rules appear in the
2198    // policy.
2199    #[test]
2200    fn parse_auditallowxperm_not_coalesced() {
2201        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
2202        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2203        let parsed_policy = &policy.0;
2204        parsed_policy.validate().expect("validate policy");
2205
2206        let class_id =
2207            find_class_by_name(&parsed_policy.classes(), "class_auditallowxperm_not_coalesced")
2208                .expect("class_auditallowxperm_not_coalesced")
2209                .id();
2210
2211        let rules: Vec<_> = parsed_policy
2212            .access_vector_rules_for_test()
2213            .filter(|rule| rule.metadata.target_class() == class_id)
2214            .collect();
2215
2216        assert_eq!(rules.len(), 2);
2217        assert!(rules[0].metadata.is_allowxperm());
2218        assert!(!rules[0].metadata.is_auditallowxperm());
2219        if let Some(xperms) = rules[0].extended_permissions() {
2220            assert_eq!(xperms.count(), 1);
2221            assert!(xperms.contains(0xabcd));
2222        } else {
2223            panic!("unexpected permission data type")
2224        }
2225        assert!(!rules[1].metadata.is_allowxperm());
2226        assert!(rules[1].metadata.is_auditallowxperm());
2227        if let Some(xperms) = rules[1].extended_permissions() {
2228            assert_eq!(xperms.count(), 1);
2229            assert!(xperms.contains(0xabcd));
2230        } else {
2231            panic!("unexpected permission data type")
2232        }
2233    }
2234}