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