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
449impl std::ops::BitAnd for XpermsBitmap {
450    type Output = Self;
451    fn bitand(self, rhs: Self) -> Self {
452        let mut result = self;
453        (0..Self::BITMAP_BLOCKS).for_each(|i| result.0[i] &= rhs.0[i]);
454        result
455    }
456}
457
458impl std::ops::BitOr for XpermsBitmap {
459    type Output = Self;
460    fn bitor(self, rhs: Self) -> Self {
461        let mut result = self;
462        (0..Self::BITMAP_BLOCKS).for_each(|i| result.0[i] |= rhs.0[i]);
463        result
464    }
465}
466
467impl std::ops::Not for XpermsBitmap {
468    type Output = Self;
469    fn not(self) -> Self {
470        let mut result = self;
471        (0..Self::BITMAP_BLOCKS).for_each(|i| result.0[i] = !result.0[i]);
472        result
473    }
474}
475
476impl std::ops::BitOrAssign<&Self> for XpermsBitmap {
477    fn bitor_assign(&mut self, rhs: &Self) {
478        (0..Self::BITMAP_BLOCKS).for_each(|i| self.0[i] |= rhs.0[i])
479    }
480}
481
482impl std::ops::SubAssign<&Self> for XpermsBitmap {
483    fn sub_assign(&mut self, rhs: &Self) {
484        (0..Self::BITMAP_BLOCKS).for_each(|i| self.0[i] = self.0[i] ^ (self.0[i] & rhs.0[i]))
485    }
486}
487
488array_type!(RoleTransitions, le::U32, RoleTransition);
489
490array_type_validate_deref_both!(RoleTransitions);
491
492impl ValidateArray<le::U32, RoleTransition> for RoleTransitions {
493    type Error = anyhow::Error;
494
495    /// [`RoleTransitions`] have no additional metadata (beyond length encoding).
496    fn validate_array(
497        _context: &PolicyValidationContext,
498        _metadata: &le::U32,
499        _items: &[RoleTransition],
500    ) -> Result<(), Self::Error> {
501        Ok(())
502    }
503}
504
505#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
506#[repr(C, packed)]
507pub(super) struct RoleTransition {
508    role: le::U32,
509    role_type: le::U32,
510    new_role: le::U32,
511    tclass: le::U32,
512}
513
514impl RoleTransition {
515    pub(super) fn current_role(&self) -> RoleId {
516        RoleId(NonZeroU32::new(self.role.get()).unwrap())
517    }
518
519    pub(super) fn type_(&self) -> TypeId {
520        TypeId(NonZeroU32::new(self.role_type.get()).unwrap())
521    }
522
523    pub(super) fn class(&self) -> ClassId {
524        ClassId(NonZeroU32::new(self.tclass.get()).unwrap())
525    }
526
527    pub(super) fn new_role(&self) -> RoleId {
528        RoleId(NonZeroU32::new(self.new_role.get()).unwrap())
529    }
530}
531
532impl Validate for RoleTransition {
533    type Error = anyhow::Error;
534
535    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
536        NonZeroU32::new(self.role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
537        NonZeroU32::new(self.role_type.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
538        NonZeroU32::new(self.tclass.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
539        NonZeroU32::new(self.new_role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
540        Ok(())
541    }
542}
543
544array_type!(RoleAllows, le::U32, RoleAllow);
545
546array_type_validate_deref_both!(RoleAllows);
547
548impl ValidateArray<le::U32, RoleAllow> for RoleAllows {
549    type Error = anyhow::Error;
550
551    /// [`RoleAllows`] have no additional metadata (beyond length encoding).
552    fn validate_array(
553        _context: &PolicyValidationContext,
554        _metadata: &le::U32,
555        _items: &[RoleAllow],
556    ) -> Result<(), Self::Error> {
557        Ok(())
558    }
559}
560
561#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
562#[repr(C, packed)]
563pub(super) struct RoleAllow {
564    role: le::U32,
565    new_role: le::U32,
566}
567
568impl RoleAllow {
569    pub(super) fn source_role(&self) -> RoleId {
570        RoleId(NonZeroU32::new(self.role.get()).unwrap())
571    }
572
573    pub(super) fn new_role(&self) -> RoleId {
574        RoleId(NonZeroU32::new(self.new_role.get()).unwrap())
575    }
576}
577
578impl Validate for RoleAllow {
579    type Error = anyhow::Error;
580
581    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
582        NonZeroU32::new(self.role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
583        NonZeroU32::new(self.new_role.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
584        Ok(())
585    }
586}
587
588#[derive(Debug, PartialEq)]
589pub(super) enum FilenameTransitionList {
590    PolicyVersionGeq33(SimpleArray<FilenameTransition>),
591    PolicyVersionLeq32(SimpleArray<DeprecatedFilenameTransition>),
592}
593
594impl Validate for FilenameTransitionList {
595    type Error = anyhow::Error;
596
597    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
598        match self {
599            Self::PolicyVersionLeq32(list) => {
600                list.validate(context).map_err(Into::<anyhow::Error>::into)
601            }
602            Self::PolicyVersionGeq33(list) => {
603                list.validate(context).map_err(Into::<anyhow::Error>::into)
604            }
605        }
606    }
607}
608
609impl Validate for FilenameTransition {
610    type Error = anyhow::Error;
611    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
612        Ok(())
613    }
614}
615
616#[derive(Debug, PartialEq)]
617pub(super) struct FilenameTransition {
618    filename: SimpleArray<u8>,
619    transition_type: le::U32,
620    transition_class: le::U32,
621    items: SimpleArray<FilenameTransitionItem>,
622}
623
624impl FilenameTransition {
625    pub(super) fn name_bytes(&self) -> &[u8] {
626        &self.filename.data
627    }
628
629    pub(super) fn target_type(&self) -> TypeId {
630        TypeId(NonZeroU32::new(self.transition_type.get()).unwrap())
631    }
632
633    pub(super) fn target_class(&self) -> ClassId {
634        ClassId(NonZeroU32::new(self.transition_class.get()).unwrap())
635    }
636
637    pub(super) fn outputs(&self) -> &[FilenameTransitionItem] {
638        &self.items.data
639    }
640}
641
642impl Parse for FilenameTransition
643where
644    SimpleArray<u8>: Parse,
645    SimpleArray<FilenameTransitionItem>: Parse,
646{
647    type Error = anyhow::Error;
648
649    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
650        let tail = bytes;
651
652        let (filename, tail) = SimpleArray::<u8>::parse(tail)
653            .map_err(Into::<anyhow::Error>::into)
654            .context("parsing filename for filename transition")?;
655
656        let (transition_type, tail) = PolicyCursor::parse::<le::U32>(tail)?;
657
658        let (transition_class, tail) = PolicyCursor::parse::<le::U32>(tail)?;
659
660        let (items, tail) = SimpleArray::<FilenameTransitionItem>::parse(tail)
661            .map_err(Into::<anyhow::Error>::into)
662            .context("parsing items for filename transition")?;
663
664        Ok((Self { filename, transition_type, transition_class, items }, tail))
665    }
666}
667
668#[derive(Debug, PartialEq)]
669pub(super) struct FilenameTransitionItem {
670    stypes: ExtensibleBitmap,
671    out_type: le::U32,
672}
673
674impl FilenameTransitionItem {
675    pub(super) fn has_source_type(&self, source_type: TypeId) -> bool {
676        self.stypes.is_set(source_type.0.get() - 1)
677    }
678
679    pub(super) fn out_type(&self) -> TypeId {
680        TypeId(NonZeroU32::new(self.out_type.get()).unwrap())
681    }
682}
683
684impl Parse for FilenameTransitionItem
685where
686    ExtensibleBitmap: Parse,
687{
688    type Error = anyhow::Error;
689
690    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
691        let tail = bytes;
692
693        let (stypes, tail) = ExtensibleBitmap::parse(tail)
694            .map_err(Into::<anyhow::Error>::into)
695            .context("parsing stypes extensible bitmap for file transition")?;
696
697        let (out_type, tail) = PolicyCursor::parse::<le::U32>(tail)?;
698
699        Ok((Self { stypes, out_type }, tail))
700    }
701}
702
703impl Validate for DeprecatedFilenameTransition {
704    type Error = anyhow::Error;
705    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
706        Ok(())
707    }
708}
709
710#[derive(Debug, PartialEq)]
711pub(super) struct DeprecatedFilenameTransition {
712    filename: SimpleArray<u8>,
713    metadata: DeprecatedFilenameTransitionMetadata,
714}
715
716impl DeprecatedFilenameTransition {
717    pub(super) fn name_bytes(&self) -> &[u8] {
718        &self.filename.data
719    }
720
721    pub(super) fn source_type(&self) -> TypeId {
722        TypeId(NonZeroU32::new(self.metadata.source_type.get()).unwrap())
723    }
724
725    pub(super) fn target_type(&self) -> TypeId {
726        TypeId(NonZeroU32::new(self.metadata.transition_type.get()).unwrap())
727    }
728
729    pub(super) fn target_class(&self) -> ClassId {
730        ClassId(NonZeroU32::new(self.metadata.transition_class.get()).unwrap())
731    }
732
733    pub(super) fn out_type(&self) -> TypeId {
734        TypeId(NonZeroU32::new(self.metadata.out_type.get()).unwrap())
735    }
736}
737
738impl Parse for DeprecatedFilenameTransition
739where
740    SimpleArray<u8>: Parse,
741{
742    type Error = anyhow::Error;
743
744    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
745        let tail = bytes;
746
747        let (filename, tail) = SimpleArray::<u8>::parse(tail)
748            .map_err(Into::<anyhow::Error>::into)
749            .context("parsing filename for deprecated filename transition")?;
750
751        let (metadata, tail) = PolicyCursor::parse::<DeprecatedFilenameTransitionMetadata>(tail)?;
752
753        Ok((Self { filename, metadata }, tail))
754    }
755}
756
757#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
758#[repr(C, packed)]
759pub(super) struct DeprecatedFilenameTransitionMetadata {
760    source_type: le::U32,
761    transition_type: le::U32,
762    transition_class: le::U32,
763    out_type: le::U32,
764}
765
766impl Validate for SimpleArray<InitialSid> {
767    type Error = anyhow::Error;
768    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
769        for initial_sid in crate::InitialSid::all_variants() {
770            self.data
771                .iter()
772                .find(|initial| initial.id().get() == *initial_sid as u32)
773                .ok_or(ValidateError::MissingInitialSid { initial_sid: *initial_sid })?;
774        }
775        Ok(())
776    }
777}
778
779#[derive(Debug, PartialEq)]
780pub(super) struct InitialSid {
781    id: le::U32,
782    context: Context,
783}
784
785impl InitialSid {
786    pub(super) fn id(&self) -> le::U32 {
787        self.id
788    }
789
790    pub(super) fn context(&self) -> &Context {
791        &self.context
792    }
793}
794
795impl Parse for InitialSid
796where
797    Context: Parse,
798{
799    type Error = anyhow::Error;
800
801    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
802        let tail = bytes;
803
804        let (id, tail) = PolicyCursor::parse::<le::U32>(tail)?;
805
806        let (context, tail) = Context::parse(tail)
807            .map_err(Into::<anyhow::Error>::into)
808            .context("parsing context for initial sid")?;
809
810        Ok((Self { id, context }, tail))
811    }
812}
813
814#[derive(Debug, PartialEq)]
815pub(super) struct Context {
816    metadata: ContextMetadata,
817    mls_range: MlsRange,
818}
819
820impl Context {
821    pub(super) fn user_id(&self) -> UserId {
822        UserId(NonZeroU32::new(self.metadata.user.get()).unwrap())
823    }
824    pub(super) fn role_id(&self) -> RoleId {
825        RoleId(NonZeroU32::new(self.metadata.role.get()).unwrap())
826    }
827    pub(super) fn type_id(&self) -> TypeId {
828        TypeId(NonZeroU32::new(self.metadata.context_type.get()).unwrap())
829    }
830    pub(super) fn low_level(&self) -> &MlsLevel {
831        self.mls_range.low()
832    }
833    pub(super) fn high_level(&self) -> &Option<MlsLevel> {
834        self.mls_range.high()
835    }
836}
837
838impl Parse for Context
839where
840    MlsRange: Parse,
841{
842    type Error = anyhow::Error;
843
844    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
845        let tail = bytes;
846
847        let (metadata, tail) =
848            PolicyCursor::parse::<ContextMetadata>(tail).context("parsing metadata for context")?;
849
850        let (mls_range, tail) = MlsRange::parse(tail)
851            .map_err(Into::<anyhow::Error>::into)
852            .context("parsing mls range for context")?;
853
854        Ok((Self { metadata, mls_range }, tail))
855    }
856}
857
858#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
859#[repr(C, packed)]
860pub(super) struct ContextMetadata {
861    user: le::U32,
862    role: le::U32,
863    context_type: le::U32,
864}
865
866impl Validate for NamedContextPair {
867    type Error = anyhow::Error;
868
869    /// TODO: Validate consistency of sequence of [`NamedContextPairs`] objects.
870    ///
871    /// TODO: Is different validation required for `filesystems` and `network_interfaces`? If so,
872    /// create wrapper types with different [`Validate`] implementations.
873    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
874        Ok(())
875    }
876}
877
878#[derive(Debug, PartialEq)]
879pub(super) struct NamedContextPair {
880    name: SimpleArray<u8>,
881    context1: Context,
882    context2: Context,
883}
884
885impl Parse for NamedContextPair
886where
887    SimpleArray<u8>: Parse,
888    Context: Parse,
889{
890    type Error = anyhow::Error;
891
892    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
893        let tail = bytes;
894
895        let (name, tail) = SimpleArray::parse(tail)
896            .map_err(Into::<anyhow::Error>::into)
897            .context("parsing filesystem context name")?;
898
899        let (context1, tail) = Context::parse(tail)
900            .map_err(Into::<anyhow::Error>::into)
901            .context("parsing first context for filesystem context")?;
902
903        let (context2, tail) = Context::parse(tail)
904            .map_err(Into::<anyhow::Error>::into)
905            .context("parsing second context for filesystem context")?;
906
907        Ok((Self { name, context1, context2 }, tail))
908    }
909}
910
911impl Validate for Port {
912    type Error = anyhow::Error;
913
914    /// TODO: Validate consistency of sequence of [`Ports`] objects.
915    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
916        Ok(())
917    }
918}
919
920#[derive(Debug, PartialEq)]
921pub(super) struct Port {
922    metadata: PortMetadata,
923    context: Context,
924}
925
926impl Parse for Port
927where
928    Context: Parse,
929{
930    type Error = anyhow::Error;
931
932    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
933        let tail = bytes;
934
935        let (metadata, tail) =
936            PolicyCursor::parse::<PortMetadata>(tail).context("parsing metadata for context")?;
937
938        let (context, tail) = Context::parse(tail)
939            .map_err(Into::<anyhow::Error>::into)
940            .context("parsing context for port")?;
941
942        Ok((Self { metadata, context }, tail))
943    }
944}
945
946#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
947#[repr(C, packed)]
948pub(super) struct PortMetadata {
949    protocol: le::U32,
950    low_port: le::U32,
951    high_port: le::U32,
952}
953
954impl Validate for Node {
955    type Error = anyhow::Error;
956
957    /// TODO: Validate consistency of sequence of [`Node`] objects.
958    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
959        Ok(())
960    }
961}
962
963#[derive(Debug, PartialEq)]
964pub(super) struct Node {
965    address: le::U32,
966    mask: le::U32,
967    context: Context,
968}
969
970impl Parse for Node
971where
972    Context: Parse,
973{
974    type Error = anyhow::Error;
975
976    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
977        let tail = bytes;
978
979        let (address, tail) = PolicyCursor::parse::<le::U32>(tail)?;
980
981        let (mask, tail) = PolicyCursor::parse::<le::U32>(tail)?;
982
983        let (context, tail) = Context::parse(tail)
984            .map_err(Into::<anyhow::Error>::into)
985            .context("parsing context for node")?;
986
987        Ok((Self { address, mask, context }, tail))
988    }
989}
990
991#[derive(Debug, PartialEq)]
992pub(super) struct FsUse {
993    behavior_and_name: Array<FsUseMetadata, u8>,
994    context: Context,
995}
996
997impl FsUse {
998    pub fn fs_type(&self) -> &[u8] {
999        &self.behavior_and_name.data
1000    }
1001
1002    pub(super) fn behavior(&self) -> FsUseType {
1003        FsUseType::try_from(self.behavior_and_name.metadata.behavior).unwrap()
1004    }
1005
1006    pub(super) fn context(&self) -> &Context {
1007        &self.context
1008    }
1009}
1010
1011impl Parse for FsUse
1012where
1013    Array<FsUseMetadata, u8>: Parse,
1014    Context: Parse,
1015{
1016    type Error = anyhow::Error;
1017
1018    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1019        let tail = bytes;
1020
1021        let (behavior_and_name, tail) = Array::<FsUseMetadata, u8>::parse(tail)
1022            .map_err(Into::<anyhow::Error>::into)
1023            .context("parsing fs use metadata")?;
1024
1025        let (context, tail) = Context::parse(tail)
1026            .map_err(Into::<anyhow::Error>::into)
1027            .context("parsing context for fs use")?;
1028
1029        Ok((Self { behavior_and_name, context }, tail))
1030    }
1031}
1032
1033impl Validate for FsUse {
1034    type Error = anyhow::Error;
1035
1036    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1037        FsUseType::try_from(self.behavior_and_name.metadata.behavior)?;
1038
1039        Ok(())
1040    }
1041}
1042
1043#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1044#[repr(C, packed)]
1045pub(super) struct FsUseMetadata {
1046    /// The type of `fs_use` statement.
1047    behavior: le::U32,
1048    /// The length of the name in the name_and_behavior field of FsUse.
1049    name_length: le::U32,
1050}
1051
1052impl Counted for FsUseMetadata {
1053    fn count(&self) -> u32 {
1054        self.name_length.get()
1055    }
1056}
1057
1058/// Discriminates among the different kinds of "fs_use_*" labeling statements in the policy; see
1059/// https://selinuxproject.org/page/FileStatements.
1060#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
1061pub enum FsUseType {
1062    Xattr = 1,
1063    Trans = 2,
1064    Task = 3,
1065}
1066
1067impl TryFrom<le::U32> for FsUseType {
1068    type Error = anyhow::Error;
1069
1070    fn try_from(value: le::U32) -> Result<Self, Self::Error> {
1071        match value.get() {
1072            1 => Ok(FsUseType::Xattr),
1073            2 => Ok(FsUseType::Trans),
1074            3 => Ok(FsUseType::Task),
1075            _ => Err(ValidateError::InvalidFsUseType { value: value.get() }.into()),
1076        }
1077    }
1078}
1079
1080impl Validate for IPv6Node {
1081    type Error = anyhow::Error;
1082
1083    /// TODO: Validate consistency of sequence of [`IPv6Node`] objects.
1084    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1085        Ok(())
1086    }
1087}
1088
1089#[derive(Debug, PartialEq)]
1090pub(super) struct IPv6Node {
1091    address: [le::U32; 4],
1092    mask: [le::U32; 4],
1093    context: Context,
1094}
1095
1096impl Parse for IPv6Node
1097where
1098    Context: Parse,
1099{
1100    type Error = anyhow::Error;
1101
1102    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1103        let tail = bytes;
1104
1105        let (address, tail) = PolicyCursor::parse::<[le::U32; 4]>(tail)?;
1106
1107        let (mask, tail) = PolicyCursor::parse::<[le::U32; 4]>(tail)?;
1108
1109        let (context, tail) = Context::parse(tail)
1110            .map_err(Into::<anyhow::Error>::into)
1111            .context("parsing context for ipv6 node")?;
1112
1113        Ok((Self { address, mask, context }, tail))
1114    }
1115}
1116
1117impl Validate for InfinitiBandPartitionKey {
1118    type Error = anyhow::Error;
1119
1120    /// TODO: Validate consistency of sequence of [`InfinitiBandPartitionKey`] objects.
1121    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1122        Ok(())
1123    }
1124}
1125
1126#[derive(Debug, PartialEq)]
1127pub(super) struct InfinitiBandPartitionKey {
1128    low: le::U32,
1129    high: le::U32,
1130    context: Context,
1131}
1132
1133impl Parse for InfinitiBandPartitionKey
1134where
1135    Context: Parse,
1136{
1137    type Error = anyhow::Error;
1138
1139    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1140        let tail = bytes;
1141
1142        let (low, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1143
1144        let (high, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1145
1146        let (context, tail) = Context::parse(tail)
1147            .map_err(Into::<anyhow::Error>::into)
1148            .context("parsing context for infiniti band partition key")?;
1149
1150        Ok((Self { low, high, context }, tail))
1151    }
1152}
1153
1154impl Validate for InfinitiBandEndPort {
1155    type Error = anyhow::Error;
1156
1157    /// TODO: Validate sequence of [`InfinitiBandEndPort`] objects.
1158    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1159        Ok(())
1160    }
1161}
1162
1163#[derive(Debug, PartialEq)]
1164pub(super) struct InfinitiBandEndPort {
1165    port_and_name: Array<InfinitiBandEndPortMetadata, u8>,
1166    context: Context,
1167}
1168
1169impl Parse for InfinitiBandEndPort
1170where
1171    Array<InfinitiBandEndPortMetadata, u8>: Parse,
1172    Context: Parse,
1173{
1174    type Error = anyhow::Error;
1175
1176    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1177        let tail = bytes;
1178
1179        let (port_and_name, tail) = Array::<InfinitiBandEndPortMetadata, u8>::parse(tail)
1180            .map_err(Into::<anyhow::Error>::into)
1181            .context("parsing infiniti band end port metadata")?;
1182
1183        let (context, tail) = Context::parse(tail)
1184            .map_err(Into::<anyhow::Error>::into)
1185            .context("parsing context for infiniti band end port")?;
1186
1187        Ok((Self { port_and_name, context }, tail))
1188    }
1189}
1190
1191#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1192#[repr(C, packed)]
1193pub(super) struct InfinitiBandEndPortMetadata {
1194    length: le::U32,
1195    port: le::U32,
1196}
1197
1198impl Counted for InfinitiBandEndPortMetadata {
1199    fn count(&self) -> u32 {
1200        self.length.get()
1201    }
1202}
1203
1204impl Validate for GenericFsContext {
1205    type Error = anyhow::Error;
1206
1207    /// TODO: Validate sequence of  [`GenericFsContext`] objects.
1208    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1209        Ok(())
1210    }
1211}
1212
1213/// Information parsed parsed from `genfscon [fs_type] [partial_path] [fs_context]` statements
1214/// about a specific filesystem type.
1215#[derive(Debug)]
1216pub(super) struct GenericFsContext {
1217    fs_type: SimpleArray<u8>,
1218    fs_context: SimpleArrayView<FsContext>,
1219}
1220
1221impl GenericFsContext {
1222    /// Returns the `fs_type` representation to be used when looking up in a CustomKeyHashedView.
1223    pub(super) fn for_query(fs_type: &str) -> SimpleArray<u8> {
1224        Array { data: fs_type.as_bytes().to_vec(), metadata: le::U32::new(fs_type.len() as u32) }
1225    }
1226}
1227
1228impl Hashable for GenericFsContext {
1229    type Key = SimpleArray<u8>;
1230    type Value = FsContext;
1231
1232    fn key(&self) -> &Self::Key {
1233        &self.fs_type
1234    }
1235
1236    fn values(&self) -> &SimpleArrayView<Self::Value> {
1237        &self.fs_context
1238    }
1239}
1240
1241impl Eq for SimpleArray<u8> {}
1242
1243impl Hash for SimpleArray<u8> {
1244    fn hash<H: Hasher>(&self, state: &mut H) {
1245        self.data.hash(state);
1246    }
1247}
1248
1249impl Parse for GenericFsContext {
1250    type Error = anyhow::Error;
1251
1252    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1253        let tail = bytes;
1254
1255        let (fs_type, tail) = SimpleArray::<u8>::parse(tail)
1256            .map_err(Into::<anyhow::Error>::into)
1257            .context("parsing fs_type for generic fs context")?;
1258
1259        let (fs_context, tail) = SimpleArrayView::<FsContext>::parse(tail)
1260            .map_err(Into::<anyhow::Error>::into)
1261            .context("parsing fs_context for generic fs context")?;
1262
1263        Ok((Self { fs_type, fs_context }, tail))
1264    }
1265}
1266
1267#[derive(Debug, PartialEq)]
1268pub(super) struct FsContext {
1269    /// The partial path, relative to the root of the filesystem. The partial path can only be set for
1270    /// virtual filesystems, like `proc/`. Otherwise, this must be `/`
1271    partial_path: SimpleArray<u8>,
1272    /// Optional. When provided, the context will only be applied to files of this type. Allowed files
1273    /// types are: blk_file, chr_file, dir, fifo_file, lnk_file, sock_file, file. When set to 0, the
1274    /// context applies to all file types.
1275    class: le::U32,
1276    /// The security context allocated to the filesystem.
1277    context: Context,
1278}
1279
1280impl FsContext {
1281    pub(super) fn partial_path(&self) -> &[u8] {
1282        &self.partial_path.data
1283    }
1284
1285    pub(super) fn context(&self) -> &Context {
1286        &self.context
1287    }
1288
1289    pub(super) fn class(&self) -> Option<ClassId> {
1290        NonZeroU32::new(self.class.into()).map(ClassId)
1291    }
1292}
1293
1294impl Parse for FsContext
1295where
1296    SimpleArray<u8>: Parse,
1297    Context: Parse,
1298{
1299    type Error = anyhow::Error;
1300
1301    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1302        let tail = bytes;
1303
1304        let (partial_path, tail) = SimpleArray::<u8>::parse(tail)
1305            .map_err(Into::<anyhow::Error>::into)
1306            .context("parsing filesystem context partial path")?;
1307
1308        let (class, tail) = PolicyCursor::parse::<le::U32>(tail)?;
1309
1310        let (context, tail) = Context::parse(tail)
1311            .map_err(Into::<anyhow::Error>::into)
1312            .context("parsing context for filesystem context")?;
1313
1314        Ok((Self { partial_path, class, context }, tail))
1315    }
1316}
1317
1318impl Validate for FsContext {
1319    type Error = anyhow::Error;
1320
1321    // TODO: validate [`FsContext`].
1322    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1323        Ok(())
1324    }
1325}
1326
1327impl Walk for FsContext {
1328    fn walk(policy_data: &PolicyData, offset: PolicyOffset) -> PolicyOffset {
1329        let cursor = PolicyCursor::new_at(policy_data, offset);
1330        let (_, tail) = FsContext::parse(cursor)
1331            .map_err(Into::<anyhow::Error>::into)
1332            .expect("policy should be valid");
1333        tail.offset()
1334    }
1335}
1336
1337impl Validate for RangeTransition {
1338    type Error = anyhow::Error;
1339    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
1340        if self.metadata.target_class.get() == 0 {
1341            return Err(ValidateError::NonOptionalIdIsZero.into());
1342        }
1343        Ok(())
1344    }
1345}
1346
1347#[derive(Debug, PartialEq)]
1348pub(super) struct RangeTransition {
1349    metadata: RangeTransitionMetadata,
1350    mls_range: MlsRange,
1351}
1352
1353impl RangeTransition {
1354    pub fn source_type(&self) -> TypeId {
1355        TypeId(NonZeroU32::new(self.metadata.source_type.get()).unwrap())
1356    }
1357
1358    pub fn target_type(&self) -> TypeId {
1359        TypeId(NonZeroU32::new(self.metadata.target_type.get()).unwrap())
1360    }
1361
1362    pub fn target_class(&self) -> ClassId {
1363        ClassId(NonZeroU32::new(self.metadata.target_class.get()).unwrap())
1364    }
1365
1366    pub fn mls_range(&self) -> &MlsRange {
1367        &self.mls_range
1368    }
1369}
1370
1371impl Parse for RangeTransition
1372where
1373    MlsRange: Parse,
1374{
1375    type Error = anyhow::Error;
1376
1377    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
1378        let tail = bytes;
1379
1380        let (metadata, tail) = PolicyCursor::parse::<RangeTransitionMetadata>(tail)
1381            .context("parsing range transition metadata")?;
1382
1383        let (mls_range, tail) = MlsRange::parse(tail)
1384            .map_err(Into::<anyhow::Error>::into)
1385            .context("parsing mls range for range transition")?;
1386
1387        Ok((Self { metadata, mls_range }, tail))
1388    }
1389}
1390
1391#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1392#[repr(C, packed)]
1393pub(super) struct RangeTransitionMetadata {
1394    source_type: le::U32,
1395    target_type: le::U32,
1396    target_class: le::U32,
1397}
1398
1399#[cfg(test)]
1400pub(super) mod testing {
1401    use super::AccessVectorRule;
1402    use std::cmp::Ordering;
1403
1404    pub(in super::super) fn access_vector_rule_ordering(
1405        left: &AccessVectorRule,
1406        right: &AccessVectorRule,
1407    ) -> Ordering {
1408        (
1409            left.metadata.source_type,
1410            left.metadata.target_type,
1411            left.metadata.class,
1412            left.metadata.access_vector_rule_type,
1413        )
1414            .cmp(&(
1415                right.metadata.source_type,
1416                right.metadata.target_type,
1417                right.metadata.class,
1418                right.metadata.access_vector_rule_type,
1419            ))
1420    }
1421}
1422
1423#[cfg(test)]
1424mod tests {
1425    use super::super::{ClassId, find_class_by_name, parse_policy_by_value};
1426    use super::{
1427        ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM, ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM,
1428        ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES,
1429        XPERMS_TYPE_IOCTL_PREFIXES, XPERMS_TYPE_NLMSG,
1430    };
1431    use std::num::NonZeroU32;
1432
1433    impl super::AccessVectorRuleMetadata {
1434        /// Returns whether this access vector rule comes from an
1435        /// `allowxperm [source] [target]:[class] [permission] {
1436        /// [extended_permissions] };` policy statement.
1437        pub fn is_allowxperm(&self) -> bool {
1438            (self.access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM) != 0
1439        }
1440
1441        /// Returns whether this access vector rule comes from an
1442        /// `auditallowxperm [source] [target]:[class] [permission] {
1443        /// [extended_permissions] };` policy statement.
1444        pub fn is_auditallowxperm(&self) -> bool {
1445            (self.access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM) != 0
1446        }
1447
1448        /// Returns whether this access vector rule comes from a
1449        /// `dontauditxperm [source] [target]:[class] [permission] {
1450        /// [extended_permissions] };` policy statement.
1451        pub fn is_dontauditxperm(&self) -> bool {
1452            (self.access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM) != 0
1453        }
1454
1455        /// Returns the target class id in this access vector rule. This id
1456        /// corresponds to the [`super::symbols::Class`] `id()` of some class in the
1457        /// same policy. Although the index is returned as a 32-bit value, the field
1458        /// itself is 16-bit
1459        pub fn target_class(&self) -> ClassId {
1460            ClassId(NonZeroU32::new(self.class.into()).unwrap())
1461        }
1462    }
1463
1464    #[test]
1465    fn parse_allowxperm_one_ioctl() {
1466        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1467        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1468        let parsed_policy = &policy.0;
1469        parsed_policy.validate().expect("validate policy");
1470
1471        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_ioctl")
1472            .expect("look up class_one_ioctl")
1473            .id();
1474
1475        let rules: Vec<_> = parsed_policy
1476            .access_vector_rules_for_test()
1477            .filter(|rule| rule.metadata.target_class() == class_id)
1478            .collect();
1479
1480        assert_eq!(rules.len(), 1);
1481        assert!(rules[0].metadata.is_allowxperm());
1482        if let Some(xperms) = rules[0].extended_permissions() {
1483            assert_eq!(xperms.count(), 1);
1484            assert!(xperms.contains(0xabcd));
1485        } else {
1486            panic!("unexpected permission data type")
1487        }
1488    }
1489
1490    // `ioctl` extended permissions that are declared in the same rule, and have the same
1491    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1492    #[test]
1493    fn parse_allowxperm_two_ioctls_same_range() {
1494        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1495        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1496        let parsed_policy = &policy.0;
1497        parsed_policy.validate().expect("validate policy");
1498
1499        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_ioctls_same_range")
1500            .expect("look up class_two_ioctls_same_range")
1501            .id();
1502
1503        let rules: Vec<_> = parsed_policy
1504            .access_vector_rules_for_test()
1505            .filter(|rule| rule.metadata.target_class() == class_id)
1506            .collect();
1507
1508        assert_eq!(rules.len(), 1);
1509        assert!(rules[0].metadata.is_allowxperm());
1510        if let Some(xperms) = rules[0].extended_permissions() {
1511            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1512            assert_eq!(xperms.xperms_optional_prefix, 0x12);
1513            assert_eq!(xperms.count(), 2);
1514            assert!(xperms.contains(0x1234));
1515            assert!(xperms.contains(0x1256));
1516        } else {
1517            panic!("unexpected permission data type")
1518        }
1519    }
1520
1521    // `ioctl` extended permissions that are declared in different rules, but that have the same
1522    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1523    #[test]
1524    fn parse_allowxperm_two_ioctls_same_range_diff_rules() {
1525        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1526        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1527        let parsed_policy = &policy.0;
1528        parsed_policy.validate().expect("validate policy");
1529
1530        let class_id =
1531            find_class_by_name(&parsed_policy.classes(), "class_four_ioctls_same_range_diff_rules")
1532                .expect("look up class_four_ioctls_same_range_diff_rules")
1533                .id();
1534
1535        let rules: Vec<_> = parsed_policy
1536            .access_vector_rules_for_test()
1537            .filter(|rule| rule.metadata.target_class() == class_id)
1538            .collect();
1539
1540        assert_eq!(rules.len(), 1);
1541        assert!(rules[0].metadata.is_allowxperm());
1542        if let Some(xperms) = rules[0].extended_permissions() {
1543            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1544            assert_eq!(xperms.xperms_optional_prefix, 0x30);
1545            assert_eq!(xperms.count(), 4);
1546            assert!(xperms.contains(0x3008));
1547            assert!(xperms.contains(0x3009));
1548            assert!(xperms.contains(0x3011));
1549            assert!(xperms.contains(0x3013));
1550        } else {
1551            panic!("unexpected permission data type")
1552        }
1553    }
1554
1555    // `ioctl` extended permissions that are declared in the same rule, and have different
1556    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1557    #[test]
1558    fn parse_allowxperm_two_ioctls_different_range() {
1559        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1560        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1561        let parsed_policy = &policy.0;
1562        parsed_policy.validate().expect("validate policy");
1563
1564        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_ioctls_diff_range")
1565            .expect("look up class_two_ioctls_diff_range")
1566            .id();
1567
1568        let rules: Vec<_> = parsed_policy
1569            .access_vector_rules_for_test()
1570            .filter(|rule| rule.metadata.target_class() == class_id)
1571            .collect();
1572
1573        assert_eq!(rules.len(), 2);
1574        assert!(rules[0].metadata.is_allowxperm());
1575        if let Some(xperms) = rules[0].extended_permissions() {
1576            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1577            assert_eq!(xperms.xperms_optional_prefix, 0x56);
1578            assert_eq!(xperms.count(), 1);
1579            assert!(xperms.contains(0x5678));
1580        } else {
1581            panic!("unexpected permission data type")
1582        }
1583        assert!(rules[1].metadata.is_allowxperm());
1584        if let Some(xperms) = rules[1].extended_permissions() {
1585            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1586            assert_eq!(xperms.xperms_optional_prefix, 0x12);
1587            assert_eq!(xperms.count(), 1);
1588            assert!(xperms.contains(0x1234));
1589        } else {
1590            panic!("unexpected permission data type")
1591        }
1592    }
1593
1594    // If a set of `ioctl` extended permissions consists of all xperms with a given high byte,
1595    // then it is represented by one `AccessVectorRule`.
1596    #[test]
1597    fn parse_allowxperm_one_driver_range() {
1598        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1599        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1600        let parsed_policy = &policy.0;
1601        parsed_policy.validate().expect("validate policy");
1602
1603        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_driver_range")
1604            .expect("look up class_one_driver_range")
1605            .id();
1606
1607        let rules: Vec<_> = parsed_policy
1608            .access_vector_rules_for_test()
1609            .filter(|rule| rule.metadata.target_class() == class_id)
1610            .collect();
1611
1612        assert_eq!(rules.len(), 1);
1613        assert!(rules[0].metadata.is_allowxperm());
1614        if let Some(xperms) = rules[0].extended_permissions() {
1615            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1616            assert_eq!(xperms.count(), 0x100);
1617            assert!(xperms.contains(0x1000));
1618            assert!(xperms.contains(0x10ab));
1619        } else {
1620            panic!("unexpected permission data type")
1621        }
1622    }
1623
1624    // If a rule grants `ioctl` extended permissions to a wide range that does not fall cleanly on
1625    // divisible-by-256 boundaries, it gets represented in the policy as three `AccessVectorRule`s:
1626    // two for the smaller subranges at the ends and one for the large subrange in the middle.
1627    #[test]
1628    fn parse_allowxperm_most_ioctls() {
1629        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1630        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1631        let parsed_policy = &policy.0;
1632        parsed_policy.validate().expect("validate policy");
1633
1634        let class_id = find_class_by_name(&parsed_policy.classes(), "class_most_ioctls")
1635            .expect("look up class_most_ioctls")
1636            .id();
1637
1638        let rules: Vec<_> = parsed_policy
1639            .access_vector_rules_for_test()
1640            .filter(|rule| rule.metadata.target_class() == class_id)
1641            .collect();
1642
1643        assert_eq!(rules.len(), 3);
1644        assert!(rules[0].metadata.is_allowxperm());
1645        if let Some(xperms) = rules[0].extended_permissions() {
1646            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1647            assert_eq!(xperms.xperms_optional_prefix, 0xff);
1648            assert_eq!(xperms.count(), 0xfe);
1649            for xperm in 0xff00..0xfffd {
1650                assert!(xperms.contains(xperm));
1651            }
1652        } else {
1653            panic!("unexpected permission data type")
1654        }
1655        if let Some(xperms) = rules[1].extended_permissions() {
1656            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1657            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1658            assert_eq!(xperms.count(), 0xfe);
1659            for xperm in 0x0002..0x0100 {
1660                assert!(xperms.contains(xperm));
1661            }
1662        } else {
1663            panic!("unexpected permission data type")
1664        }
1665        if let Some(xperms) = rules[2].extended_permissions() {
1666            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1667            assert_eq!(xperms.count(), 0xfe00);
1668            for xperm in 0x0100..0xff00 {
1669                assert!(xperms.contains(xperm));
1670            }
1671        } else {
1672            panic!("unexpected permission data type")
1673        }
1674    }
1675
1676    // If a rule grants `ioctl` extended permissions to two wide ranges that do not fall cleanly on
1677    // divisible-by-256 boundaries, they get represented in the policy as five `AccessVectorRule`s:
1678    // four for the smaller subranges at the ends and one for the two large subranges.
1679    #[test]
1680    fn parse_allowxperm_most_ioctls_with_hole() {
1681        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1682        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1683        let parsed_policy = &policy.0;
1684        parsed_policy.validate().expect("validate policy");
1685
1686        let class_id = find_class_by_name(&parsed_policy.classes(), "class_most_ioctls_with_hole")
1687            .expect("look up class_most_ioctls_with_hole")
1688            .id();
1689
1690        let rules: Vec<_> = parsed_policy
1691            .access_vector_rules_for_test()
1692            .filter(|rule| rule.metadata.target_class() == class_id)
1693            .collect();
1694
1695        assert_eq!(rules.len(), 5);
1696        assert!(rules[0].metadata.is_allowxperm());
1697        if let Some(xperms) = rules[0].extended_permissions() {
1698            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1699            assert_eq!(xperms.xperms_optional_prefix, 0xff);
1700            assert_eq!(xperms.count(), 0xfe);
1701            for xperm in 0xff00..0xfffd {
1702                assert!(xperms.contains(xperm));
1703            }
1704        } else {
1705            panic!("unexpected permission data type")
1706        }
1707        if let Some(xperms) = rules[1].extended_permissions() {
1708            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1709            assert_eq!(xperms.xperms_optional_prefix, 0x40);
1710            assert_eq!(xperms.count(), 0xfe);
1711            for xperm in 0x4002..0x4100 {
1712                assert!(xperms.contains(xperm));
1713            }
1714        } else {
1715            panic!("unexpected permission data type")
1716        }
1717        assert!(rules[0].metadata.is_allowxperm());
1718        if let Some(xperms) = rules[2].extended_permissions() {
1719            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1720            assert_eq!(xperms.xperms_optional_prefix, 0x2f);
1721            assert_eq!(xperms.count(), 0xfe);
1722            for xperm in 0x2f00..0x2ffd {
1723                assert!(xperms.contains(xperm));
1724            }
1725        } else {
1726            panic!("unexpected permission data type")
1727        }
1728        if let Some(xperms) = rules[3].extended_permissions() {
1729            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1730            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1731            assert_eq!(xperms.count(), 0xfe);
1732            for xperm in 0x0002..0x0100 {
1733                assert!(xperms.contains(xperm));
1734            }
1735        } else {
1736            panic!("unexpected permission data type")
1737        }
1738        if let Some(xperms) = rules[4].extended_permissions() {
1739            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1740            assert_eq!(xperms.count(), 0xec00);
1741            for xperm in 0x0100..0x2f00 {
1742                assert!(xperms.contains(xperm));
1743            }
1744            for xperm in 0x4100..0xff00 {
1745                assert!(xperms.contains(xperm));
1746            }
1747        } else {
1748            panic!("unexpected permission data type")
1749        }
1750    }
1751
1752    // If a set of `ioctl` extended permissions contains all 16-bit xperms, then it is
1753    // then it is represented by one `AccessVectorRule`. (More generally, the representation
1754    // is a single `AccessVectorRule` as long as the set either fully includes or fully
1755    // excludes each 8-bit prefix range.)
1756    #[test]
1757    fn parse_allowxperm_all_ioctls() {
1758        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1759        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1760        let parsed_policy = &policy.0;
1761        parsed_policy.validate().expect("validate policy");
1762
1763        let class_id = find_class_by_name(&parsed_policy.classes(), "class_all_ioctls")
1764            .expect("look up class_all_ioctls")
1765            .id();
1766
1767        let rules: Vec<_> = parsed_policy
1768            .access_vector_rules_for_test()
1769            .filter(|rule| rule.metadata.target_class() == class_id)
1770            .collect();
1771
1772        assert_eq!(rules.len(), 1);
1773        assert!(rules[0].metadata.is_allowxperm());
1774        if let Some(xperms) = rules[0].extended_permissions() {
1775            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1776            assert_eq!(xperms.count(), 0x10000);
1777        } else {
1778            panic!("unexpected permission data type")
1779        }
1780    }
1781
1782    #[test]
1783    fn parse_allowxperm_one_nlmsg() {
1784        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1785        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1786        let parsed_policy = &policy.0;
1787        parsed_policy.validate().expect("validate policy");
1788
1789        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_nlmsg")
1790            .expect("look up class_one_nlmsg")
1791            .id();
1792
1793        let rules: Vec<_> = parsed_policy
1794            .access_vector_rules_for_test()
1795            .filter(|rule| rule.metadata.target_class() == class_id)
1796            .collect();
1797
1798        assert_eq!(rules.len(), 1);
1799        assert!(rules[0].metadata.is_allowxperm());
1800        if let Some(xperms) = rules[0].extended_permissions() {
1801            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1802            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1803            assert_eq!(xperms.count(), 1);
1804            assert!(xperms.contains(0x12));
1805        } else {
1806            panic!("unexpected permission data type")
1807        }
1808    }
1809
1810    // `nlmsg` extended permissions that are declared in the same rule, and have the same
1811    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1812    #[test]
1813    fn parse_allowxperm_two_nlmsg_same_range() {
1814        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1815        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1816        let parsed_policy = &policy.0;
1817        parsed_policy.validate().expect("validate policy");
1818
1819        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_nlmsg_same_range")
1820            .expect("look up class_two_nlmsg_same_range")
1821            .id();
1822
1823        let rules: Vec<_> = parsed_policy
1824            .access_vector_rules_for_test()
1825            .filter(|rule| rule.metadata.target_class() == class_id)
1826            .collect();
1827
1828        assert_eq!(rules.len(), 1);
1829        assert!(rules[0].metadata.is_allowxperm());
1830        if let Some(xperms) = rules[0].extended_permissions() {
1831            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1832            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1833            assert_eq!(xperms.count(), 2);
1834            assert!(xperms.contains(0x12));
1835            assert!(xperms.contains(0x24));
1836        } else {
1837            panic!("unexpected permission data type")
1838        }
1839    }
1840
1841    // `nlmsg` extended permissions that are declared in the same rule, and have different
1842    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1843    #[test]
1844    fn parse_allowxperm_two_nlmsg_different_range() {
1845        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1846        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1847        let parsed_policy = &policy.0;
1848        parsed_policy.validate().expect("validate policy");
1849
1850        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_nlmsg_diff_range")
1851            .expect("look up class_two_nlmsg_diff_range")
1852            .id();
1853
1854        let rules: Vec<_> = parsed_policy
1855            .access_vector_rules_for_test()
1856            .filter(|rule| rule.metadata.target_class() == class_id)
1857            .collect();
1858
1859        assert_eq!(rules.len(), 2);
1860        assert!(rules[0].metadata.is_allowxperm());
1861        if let Some(xperms) = rules[0].extended_permissions() {
1862            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1863            assert_eq!(xperms.xperms_optional_prefix, 0x10);
1864            assert_eq!(xperms.count(), 1);
1865            assert!(xperms.contains(0x1024));
1866        } else {
1867            panic!("unexpected permission data type")
1868        }
1869        assert!(rules[1].metadata.is_allowxperm());
1870        if let Some(xperms) = rules[1].extended_permissions() {
1871            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1872            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1873            assert_eq!(xperms.count(), 1);
1874            assert!(xperms.contains(0x12));
1875        } else {
1876            panic!("unexpected permission data type")
1877        }
1878    }
1879
1880    // The set of `nlmsg` extended permissions with a given high byte is represented by
1881    // a single `AccessVectorRule` in the compiled policy.
1882    #[test]
1883    fn parse_allowxperm_one_nlmsg_range() {
1884        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1885        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1886        let parsed_policy = &policy.0;
1887        parsed_policy.validate().expect("validate policy");
1888
1889        let class_id = find_class_by_name(&parsed_policy.classes(), "class_one_nlmsg_range")
1890            .expect("look up class_one_nlmsg_range")
1891            .id();
1892
1893        let rules: Vec<_> = parsed_policy
1894            .access_vector_rules_for_test()
1895            .filter(|rule| rule.metadata.target_class() == class_id)
1896            .collect();
1897
1898        assert_eq!(rules.len(), 1);
1899        assert!(rules[0].metadata.is_allowxperm());
1900        if let Some(xperms) = rules[0].extended_permissions() {
1901            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1902            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1903            assert_eq!(xperms.count(), 0x100);
1904            for i in 0x0..0xff {
1905                assert!(xperms.contains(i), "{i}");
1906            }
1907        } else {
1908            panic!("unexpected permission data type")
1909        }
1910    }
1911
1912    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
1913    // of 2 given prefix bytes is represented by 2 `AccessVectorRule`s in the compiled policy.
1914    //
1915    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
1916    // are not expected to appear in policies.
1917    #[test]
1918    fn parse_allowxperm_two_nlmsg_ranges() {
1919        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1920        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1921        let parsed_policy = &policy.0;
1922        parsed_policy.validate().expect("validate policy");
1923
1924        let class_id = find_class_by_name(&parsed_policy.classes(), "class_two_nlmsg_ranges")
1925            .expect("look up class_two_nlmsg_ranges")
1926            .id();
1927
1928        let rules: Vec<_> = parsed_policy
1929            .access_vector_rules_for_test()
1930            .filter(|rule| rule.metadata.target_class() == class_id)
1931            .collect();
1932
1933        assert_eq!(rules.len(), 2);
1934        assert!(rules[0].metadata.is_allowxperm());
1935        if let Some(xperms) = rules[0].extended_permissions() {
1936            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1937            assert_eq!(xperms.xperms_optional_prefix, 0x01);
1938            assert_eq!(xperms.count(), 0x100);
1939            for i in 0x0100..0x01ff {
1940                assert!(xperms.contains(i), "{i}");
1941            }
1942        } else {
1943            panic!("unexpected permission data type")
1944        }
1945        if let Some(xperms) = rules[1].extended_permissions() {
1946            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1947            assert_eq!(xperms.xperms_optional_prefix, 0x00);
1948            assert_eq!(xperms.count(), 0x100);
1949            for i in 0x0..0xff {
1950                assert!(xperms.contains(i), "{i}");
1951            }
1952        } else {
1953            panic!("unexpected permission data type")
1954        }
1955    }
1956
1957    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
1958    // of 3 non-consecutive prefix bytes is represented by 3 `AccessVectorRule`s in the
1959    // compiled policy.
1960    //
1961    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
1962    // are not expected to appear in policies.
1963    #[test]
1964    fn parse_allowxperm_three_separate_nlmsg_ranges() {
1965        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1966        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1967        let parsed_policy = &policy.0;
1968        parsed_policy.validate().expect("validate policy");
1969
1970        let class_id =
1971            find_class_by_name(&parsed_policy.classes(), "class_three_separate_nlmsg_ranges")
1972                .expect("look up class_three_separate_nlmsg_ranges")
1973                .id();
1974
1975        let rules: Vec<_> = parsed_policy
1976            .access_vector_rules_for_test()
1977            .filter(|rule| rule.metadata.target_class() == class_id)
1978            .collect();
1979
1980        assert_eq!(rules.len(), 3);
1981        assert!(rules[0].metadata.is_allowxperm());
1982        if let Some(xperms) = rules[0].extended_permissions() {
1983            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1984            assert_eq!(xperms.xperms_optional_prefix, 0x20);
1985            assert_eq!(xperms.count(), 0x100);
1986            for i in 0x2000..0x20ff {
1987                assert!(xperms.contains(i), "{i}");
1988            }
1989        } else {
1990            panic!("unexpected permission data type")
1991        }
1992        if let Some(xperms) = rules[1].extended_permissions() {
1993            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1994            assert_eq!(xperms.xperms_optional_prefix, 0x10);
1995            assert_eq!(xperms.count(), 0x100);
1996            for i in 0x1000..0x10ff {
1997                assert!(xperms.contains(i), "{i}");
1998            }
1999        } else {
2000            panic!("unexpected permission data type")
2001        }
2002        if let Some(xperms) = rules[2].extended_permissions() {
2003            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2004            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2005            assert_eq!(xperms.count(), 0x100);
2006            for i in 0x0..0xff {
2007                assert!(xperms.contains(i), "{i}");
2008            }
2009        } else {
2010            panic!("unexpected permission data type")
2011        }
2012    }
2013
2014    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
2015    // of 3 (or more) consecutive prefix bytes is represented by 2 `AccessVectorRule`s in the
2016    // compiled policy, one for the smallest prefix byte and one for the largest.
2017    //
2018    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
2019    // are not expected to appear in policies.
2020    #[test]
2021    fn parse_allowxperm_three_contiguous_nlmsg_ranges() {
2022        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2023        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2024        let parsed_policy = &policy.0;
2025        parsed_policy.validate().expect("validate policy");
2026
2027        let class_id =
2028            find_class_by_name(&parsed_policy.classes(), "class_three_contiguous_nlmsg_ranges")
2029                .expect("look up class_three_contiguous_nlmsg_ranges")
2030                .id();
2031
2032        let rules: Vec<_> = parsed_policy
2033            .access_vector_rules_for_test()
2034            .filter(|rule| rule.metadata.target_class() == class_id)
2035            .collect();
2036
2037        assert_eq!(rules.len(), 2);
2038        assert!(rules[0].metadata.is_allowxperm());
2039        if let Some(xperms) = rules[0].extended_permissions() {
2040            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2041            assert_eq!(xperms.xperms_optional_prefix, 0x02);
2042            assert_eq!(xperms.count(), 0x100);
2043            for i in 0x0200..0x02ff {
2044                assert!(xperms.contains(i), "{i}");
2045            }
2046        } else {
2047            panic!("unexpected permission data type")
2048        }
2049        if let Some(xperms) = rules[1].extended_permissions() {
2050            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2051            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2052            assert_eq!(xperms.count(), 0x100);
2053            for i in 0x0..0xff {
2054                assert!(xperms.contains(i), "{i}");
2055            }
2056        } else {
2057            panic!("unexpected permission data type")
2058        }
2059    }
2060
2061    // The representation of extended permissions for `auditallowxperm` rules is
2062    // the same as for `allowxperm` rules.
2063    #[test]
2064    fn parse_auditallowxperm() {
2065        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2066        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2067        let parsed_policy = &policy.0;
2068        parsed_policy.validate().expect("validate policy");
2069
2070        let class_id = find_class_by_name(&parsed_policy.classes(), "class_auditallowxperm")
2071            .expect("look up class_auditallowxperm")
2072            .id();
2073
2074        let rules: Vec<_> = parsed_policy
2075            .access_vector_rules_for_test()
2076            .filter(|rule| rule.metadata.target_class() == class_id)
2077            .collect();
2078
2079        assert_eq!(rules.len(), 2);
2080        assert!(rules[0].metadata.is_auditallowxperm());
2081        if let Some(xperms) = rules[0].extended_permissions() {
2082            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2083            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2084            assert_eq!(xperms.count(), 1);
2085            assert!(xperms.contains(0x10));
2086        } else {
2087            panic!("unexpected permission data type")
2088        }
2089        if let Some(xperms) = rules[1].extended_permissions() {
2090            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
2091            assert_eq!(xperms.xperms_optional_prefix, 0x10);
2092            assert_eq!(xperms.count(), 1);
2093            assert!(xperms.contains(0x1000));
2094        } else {
2095            panic!("unexpected permission data type")
2096        }
2097    }
2098
2099    // The representation of extended permissions for `dontauditxperm` rules is
2100    // the same as for `allowxperm` rules. In particular, the `AccessVectorRule`
2101    // contains the same set of extended permissions that appears in the text
2102    // policy. (This differs from the representation of the access vector in
2103    // `AccessVectorRule`s for `dontaudit` rules, where the `AccessVectorRule`
2104    // contains the complement of the access vector that appears in the text
2105    // policy.)
2106    #[test]
2107    fn parse_dontauditxperm() {
2108        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2109        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2110        let parsed_policy = &policy.0;
2111        parsed_policy.validate().expect("validate policy");
2112
2113        let class_id = find_class_by_name(&parsed_policy.classes(), "class_dontauditxperm")
2114            .expect("look up class_dontauditxperm")
2115            .id();
2116
2117        let rules: Vec<_> = parsed_policy
2118            .access_vector_rules_for_test()
2119            .filter(|rule| rule.metadata.target_class() == class_id)
2120            .collect();
2121
2122        assert_eq!(rules.len(), 2);
2123        assert!(rules[0].metadata.is_dontauditxperm());
2124        if let Some(xperms) = rules[0].extended_permissions() {
2125            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2126            assert_eq!(xperms.xperms_optional_prefix, 0x00);
2127            assert_eq!(xperms.count(), 1);
2128            assert!(xperms.contains(0x11));
2129        } else {
2130            panic!("unexpected permission data type")
2131        }
2132        if let Some(xperms) = rules[1].extended_permissions() {
2133            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
2134            assert_eq!(xperms.xperms_optional_prefix, 0x10);
2135            assert_eq!(xperms.count(), 1);
2136            assert!(xperms.contains(0x1000));
2137        } else {
2138            panic!("unexpected permission data type")
2139        }
2140    }
2141
2142    // If an allowxperm rule and an auditallowxperm rule specify exactly the same permissions, they
2143    // are not coalesced into a single `AccessVectorRule` in the policy; two rules appear in the
2144    // policy.
2145    #[test]
2146    fn parse_auditallowxperm_not_coalesced() {
2147        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2148        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2149        let parsed_policy = &policy.0;
2150        parsed_policy.validate().expect("validate policy");
2151
2152        let class_id =
2153            find_class_by_name(&parsed_policy.classes(), "class_auditallowxperm_not_coalesced")
2154                .expect("class_auditallowxperm_not_coalesced")
2155                .id();
2156
2157        let rules: Vec<_> = parsed_policy
2158            .access_vector_rules_for_test()
2159            .filter(|rule| rule.metadata.target_class() == class_id)
2160            .collect();
2161
2162        assert_eq!(rules.len(), 2);
2163        assert!(rules[0].metadata.is_allowxperm());
2164        assert!(!rules[0].metadata.is_auditallowxperm());
2165        if let Some(xperms) = rules[0].extended_permissions() {
2166            assert_eq!(xperms.count(), 1);
2167            assert!(xperms.contains(0xabcd));
2168        } else {
2169            panic!("unexpected permission data type")
2170        }
2171        assert!(!rules[1].metadata.is_allowxperm());
2172        assert!(rules[1].metadata.is_auditallowxperm());
2173        if let Some(xperms) = rules[1].extended_permissions() {
2174            assert_eq!(xperms.count(), 1);
2175            assert!(xperms.contains(0xabcd));
2176        } else {
2177            panic!("unexpected permission data type")
2178        }
2179    }
2180}