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