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    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 different rules, but that have the same
1623    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1624    #[test]
1625    fn parse_allowxperm_two_ioctls_same_range_diff_rules() {
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 =
1632            find_class_by_name(parsed_policy.classes(), "class_four_ioctls_same_range_diff_rules")
1633                .expect("look up class_four_ioctls_same_range_diff_rules")
1634                .id();
1635
1636        let rules: Vec<_> = parsed_policy
1637            .access_vector_rules_for_test()
1638            .filter(|rule| rule.metadata.target_class() == class_id)
1639            .collect();
1640
1641        assert_eq!(rules.len(), 1);
1642        assert!(rules[0].metadata.is_allowxperm());
1643        if let Some(xperms) = rules[0].extended_permissions() {
1644            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1645            assert_eq!(xperms.count(), 4);
1646            assert!(xperms.contains(0x3008));
1647            assert!(xperms.contains(0x3009));
1648            assert!(xperms.contains(0x3011));
1649            assert!(xperms.contains(0x3013));
1650        } else {
1651            panic!("unexpected permission data type")
1652        }
1653    }
1654
1655    // `ioctl` extended permissions that are declared in the same rule, and have different
1656    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1657    #[test]
1658    fn parse_allowxperm_two_ioctls_different_range() {
1659        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1660        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1661        let parsed_policy = &policy.0;
1662        parsed_policy.validate().expect("validate policy");
1663
1664        let class_id = find_class_by_name(parsed_policy.classes(), "class_two_ioctls_diff_range")
1665            .expect("look up class_two_ioctls_diff_range")
1666            .id();
1667
1668        let rules: Vec<_> = parsed_policy
1669            .access_vector_rules_for_test()
1670            .filter(|rule| rule.metadata.target_class() == class_id)
1671            .collect();
1672
1673        assert_eq!(rules.len(), 2);
1674        assert!(rules[0].metadata.is_allowxperm());
1675        if let Some(xperms) = rules[0].extended_permissions() {
1676            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1677            assert_eq!(xperms.count(), 1);
1678            assert!(xperms.contains(0x5678));
1679        } else {
1680            panic!("unexpected permission data type")
1681        }
1682        assert!(rules[1].metadata.is_allowxperm());
1683        if let Some(xperms) = rules[1].extended_permissions() {
1684            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1685            assert_eq!(xperms.count(), 1);
1686            assert!(xperms.contains(0x1234));
1687        } else {
1688            panic!("unexpected permission data type")
1689        }
1690    }
1691
1692    // If a set of `ioctl` extended permissions consists of all xperms with a given high byte,
1693    // then it is represented by one `AccessVectorRule`.
1694    #[test]
1695    fn parse_allowxperm_one_driver_range() {
1696        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1697        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1698        let parsed_policy = &policy.0;
1699        parsed_policy.validate().expect("validate policy");
1700
1701        let class_id = find_class_by_name(parsed_policy.classes(), "class_one_driver_range")
1702            .expect("look up class_one_driver_range")
1703            .id();
1704
1705        let rules: Vec<_> = parsed_policy
1706            .access_vector_rules_for_test()
1707            .filter(|rule| rule.metadata.target_class() == class_id)
1708            .collect();
1709
1710        assert_eq!(rules.len(), 1);
1711        assert!(rules[0].metadata.is_allowxperm());
1712        if let Some(xperms) = rules[0].extended_permissions() {
1713            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1714            assert_eq!(xperms.count(), 0x100);
1715            assert!(xperms.contains(0x1000));
1716            assert!(xperms.contains(0x10ab));
1717        } else {
1718            panic!("unexpected permission data type")
1719        }
1720    }
1721
1722    // If a rule grants `ioctl` extended permissions to a wide range that does not fall cleanly on
1723    // divisible-by-256 boundaries, it gets represented in the policy as three `AccessVectorRule`s:
1724    // two for the smaller subranges at the ends and one for the large subrange in the middle.
1725    #[test]
1726    fn parse_allowxperm_most_ioctls() {
1727        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1728        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1729        let parsed_policy = &policy.0;
1730        parsed_policy.validate().expect("validate policy");
1731
1732        let class_id = find_class_by_name(parsed_policy.classes(), "class_most_ioctls")
1733            .expect("look up class_most_ioctls")
1734            .id();
1735
1736        let rules: Vec<_> = parsed_policy
1737            .access_vector_rules_for_test()
1738            .filter(|rule| rule.metadata.target_class() == class_id)
1739            .collect();
1740
1741        assert_eq!(rules.len(), 3);
1742        assert!(rules[0].metadata.is_allowxperm());
1743        if let Some(xperms) = rules[0].extended_permissions() {
1744            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1745            assert_eq!(xperms.count(), 0xfe);
1746            for xperm in 0xff00..0xfffd {
1747                assert!(xperms.contains(xperm));
1748            }
1749        } else {
1750            panic!("unexpected permission data type")
1751        }
1752        if let Some(xperms) = rules[1].extended_permissions() {
1753            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1754            assert_eq!(xperms.count(), 0xfe);
1755            for xperm in 0x0002..0x0100 {
1756                assert!(xperms.contains(xperm));
1757            }
1758        } else {
1759            panic!("unexpected permission data type")
1760        }
1761        if let Some(xperms) = rules[2].extended_permissions() {
1762            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1763            assert_eq!(xperms.count(), 0xfe00);
1764            for xperm in 0x0100..0xff00 {
1765                assert!(xperms.contains(xperm));
1766            }
1767        } else {
1768            panic!("unexpected permission data type")
1769        }
1770    }
1771
1772    // If a rule grants `ioctl` extended permissions to two wide ranges that do not fall cleanly on
1773    // divisible-by-256 boundaries, they get represented in the policy as five `AccessVectorRule`s:
1774    // four for the smaller subranges at the ends and one for the two large subranges.
1775    #[test]
1776    fn parse_allowxperm_most_ioctls_with_hole() {
1777        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1778        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1779        let parsed_policy = &policy.0;
1780        parsed_policy.validate().expect("validate policy");
1781
1782        let class_id = find_class_by_name(parsed_policy.classes(), "class_most_ioctls_with_hole")
1783            .expect("look up class_most_ioctls_with_hole")
1784            .id();
1785
1786        let rules: Vec<_> = parsed_policy
1787            .access_vector_rules_for_test()
1788            .filter(|rule| rule.metadata.target_class() == class_id)
1789            .collect();
1790
1791        assert_eq!(rules.len(), 5);
1792        assert!(rules[0].metadata.is_allowxperm());
1793        if let Some(xperms) = rules[0].extended_permissions() {
1794            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1795            assert_eq!(xperms.count(), 0xfe);
1796            for xperm in 0xff00..0xfffd {
1797                assert!(xperms.contains(xperm));
1798            }
1799        } else {
1800            panic!("unexpected permission data type")
1801        }
1802        if let Some(xperms) = rules[1].extended_permissions() {
1803            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1804            assert_eq!(xperms.count(), 0xfe);
1805            for xperm in 0x4002..0x4100 {
1806                assert!(xperms.contains(xperm));
1807            }
1808        } else {
1809            panic!("unexpected permission data type")
1810        }
1811        assert!(rules[0].metadata.is_allowxperm());
1812        if let Some(xperms) = rules[2].extended_permissions() {
1813            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1814            assert_eq!(xperms.count(), 0xfe);
1815            for xperm in 0x2f00..0x2ffd {
1816                assert!(xperms.contains(xperm));
1817            }
1818        } else {
1819            panic!("unexpected permission data type")
1820        }
1821        if let Some(xperms) = rules[3].extended_permissions() {
1822            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1823            assert_eq!(xperms.count(), 0xfe);
1824            for xperm in 0x0002..0x0100 {
1825                assert!(xperms.contains(xperm));
1826            }
1827        } else {
1828            panic!("unexpected permission data type")
1829        }
1830        if let Some(xperms) = rules[4].extended_permissions() {
1831            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1832            assert_eq!(xperms.count(), 0xec00);
1833            for xperm in 0x0100..0x2f00 {
1834                assert!(xperms.contains(xperm));
1835            }
1836            for xperm in 0x4100..0xff00 {
1837                assert!(xperms.contains(xperm));
1838            }
1839        } else {
1840            panic!("unexpected permission data type")
1841        }
1842    }
1843
1844    // If a set of `ioctl` extended permissions contains all 16-bit xperms, then it is
1845    // then it is represented by one `AccessVectorRule`. (More generally, the representation
1846    // is a single `AccessVectorRule` as long as the set either fully includes or fully
1847    // excludes each 8-bit prefix range.)
1848    #[test]
1849    fn parse_allowxperm_all_ioctls() {
1850        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1851        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1852        let parsed_policy = &policy.0;
1853        parsed_policy.validate().expect("validate policy");
1854
1855        let class_id = find_class_by_name(parsed_policy.classes(), "class_all_ioctls")
1856            .expect("look up class_all_ioctls")
1857            .id();
1858
1859        let rules: Vec<_> = parsed_policy
1860            .access_vector_rules_for_test()
1861            .filter(|rule| rule.metadata.target_class() == class_id)
1862            .collect();
1863
1864        assert_eq!(rules.len(), 1);
1865        assert!(rules[0].metadata.is_allowxperm());
1866        if let Some(xperms) = rules[0].extended_permissions() {
1867            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1868            assert_eq!(xperms.count(), 0x10000);
1869        } else {
1870            panic!("unexpected permission data type")
1871        }
1872    }
1873
1874    // Distinct xperm rules with the same source, target, class, and permission are
1875    // represented by distinct `AccessVectorRule`s in the compiled policy, even if
1876    // they have overlapping xperm ranges.
1877    #[test]
1878    fn parse_allowxperm_overlapping_ranges() {
1879        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1880        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1881        let parsed_policy = &policy.0;
1882        parsed_policy.validate().expect("validate policy");
1883
1884        let class_id = find_class_by_name(parsed_policy.classes(), "class_overlapping_ranges")
1885            .expect("look up class_overlapping_ranges")
1886            .id();
1887
1888        let rules: Vec<_> = parsed_policy
1889            .access_vector_rules_for_test()
1890            .filter(|rule| rule.metadata.target_class() == class_id)
1891            .collect();
1892
1893        assert_eq!(rules.len(), 2);
1894        assert!(rules[0].metadata.is_allowxperm());
1895        if let Some(xperms) = rules[0].extended_permissions() {
1896            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIXES);
1897            assert_eq!(xperms.count(), 0x100);
1898            // Any ioctl in the range 0x10?? should be in the set.
1899            assert!(xperms.contains(0x1000));
1900            assert!(xperms.contains(0x10ab));
1901        } else {
1902            panic!("unexpected permission data type")
1903        }
1904        assert!(rules[1].metadata.is_allowxperm());
1905        if let Some(xperms) = rules[1].extended_permissions() {
1906            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1907            assert_eq!(xperms.count(), 2);
1908            assert!(xperms.contains(0x1000));
1909            assert!(xperms.contains(0x1001));
1910        } else {
1911            panic!("unexpected permission data type")
1912        }
1913    }
1914
1915    #[test]
1916    fn parse_allowxperm_one_nlmsg() {
1917        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1918        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1919        let parsed_policy = &policy.0;
1920        parsed_policy.validate().expect("validate policy");
1921
1922        let class_id = find_class_by_name(parsed_policy.classes(), "class_one_nlmsg")
1923            .expect("look up class_one_nlmsg")
1924            .id();
1925
1926        let rules: Vec<_> = parsed_policy
1927            .access_vector_rules_for_test()
1928            .filter(|rule| rule.metadata.target_class() == class_id)
1929            .collect();
1930
1931        assert_eq!(rules.len(), 1);
1932        assert!(rules[0].metadata.is_allowxperm());
1933        if let Some(xperms) = rules[0].extended_permissions() {
1934            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1935            assert_eq!(xperms.count(), 1);
1936            assert!(xperms.contains(0x12));
1937        } else {
1938            panic!("unexpected permission data type")
1939        }
1940    }
1941
1942    // `nlmsg` extended permissions that are declared in the same rule, and have the same
1943    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1944    #[test]
1945    fn parse_allowxperm_two_nlmsg_same_range() {
1946        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1947        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1948        let parsed_policy = &policy.0;
1949        parsed_policy.validate().expect("validate policy");
1950
1951        let class_id = find_class_by_name(parsed_policy.classes(), "class_two_nlmsg_same_range")
1952            .expect("look up class_two_nlmsg_same_range")
1953            .id();
1954
1955        let rules: Vec<_> = parsed_policy
1956            .access_vector_rules_for_test()
1957            .filter(|rule| rule.metadata.target_class() == class_id)
1958            .collect();
1959
1960        assert_eq!(rules.len(), 1);
1961        assert!(rules[0].metadata.is_allowxperm());
1962        if let Some(xperms) = rules[0].extended_permissions() {
1963            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1964            assert_eq!(xperms.count(), 2);
1965            assert!(xperms.contains(0x12));
1966            assert!(xperms.contains(0x24));
1967        } else {
1968            panic!("unexpected permission data type")
1969        }
1970    }
1971
1972    // `nlmsg` extended permissions that are declared in the same rule, and have different
1973    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1974    #[test]
1975    fn parse_allowxperm_two_nlmsg_different_range() {
1976        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1977        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1978        let parsed_policy = &policy.0;
1979        parsed_policy.validate().expect("validate policy");
1980
1981        let class_id = find_class_by_name(parsed_policy.classes(), "class_two_nlmsg_diff_range")
1982            .expect("look up class_two_nlmsg_diff_range")
1983            .id();
1984
1985        let rules: Vec<_> = parsed_policy
1986            .access_vector_rules_for_test()
1987            .filter(|rule| rule.metadata.target_class() == class_id)
1988            .collect();
1989
1990        assert_eq!(rules.len(), 2);
1991        assert!(rules[0].metadata.is_allowxperm());
1992        if let Some(xperms) = rules[0].extended_permissions() {
1993            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
1994            assert_eq!(xperms.count(), 1);
1995            assert!(xperms.contains(0x1024));
1996        } else {
1997            panic!("unexpected permission data type")
1998        }
1999        assert!(rules[1].metadata.is_allowxperm());
2000        if let Some(xperms) = rules[1].extended_permissions() {
2001            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2002            assert_eq!(xperms.count(), 1);
2003            assert!(xperms.contains(0x12));
2004        } else {
2005            panic!("unexpected permission data type")
2006        }
2007    }
2008
2009    // The set of `nlmsg` extended permissions with a given high byte is represented by
2010    // a single `AccessVectorRule` in the compiled policy.
2011    #[test]
2012    fn parse_allowxperm_one_nlmsg_range() {
2013        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2014        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2015        let parsed_policy = &policy.0;
2016        parsed_policy.validate().expect("validate policy");
2017
2018        let class_id = find_class_by_name(parsed_policy.classes(), "class_one_nlmsg_range")
2019            .expect("look up class_one_nlmsg_range")
2020            .id();
2021
2022        let rules: Vec<_> = parsed_policy
2023            .access_vector_rules_for_test()
2024            .filter(|rule| rule.metadata.target_class() == class_id)
2025            .collect();
2026
2027        assert_eq!(rules.len(), 1);
2028        assert!(rules[0].metadata.is_allowxperm());
2029        if let Some(xperms) = rules[0].extended_permissions() {
2030            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2031            assert_eq!(xperms.count(), 0x100);
2032            for i in 0x0..0xff {
2033                assert!(xperms.contains(i), "{i}");
2034            }
2035        } else {
2036            panic!("unexpected permission data type")
2037        }
2038    }
2039
2040    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
2041    // of 2 given prefix bytes is represented by 2 `AccessVectorRule`s in the compiled policy.
2042    //
2043    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
2044    // are not expected to appear in policies.
2045    #[test]
2046    fn parse_allowxperm_two_nlmsg_ranges() {
2047        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2048        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2049        let parsed_policy = &policy.0;
2050        parsed_policy.validate().expect("validate policy");
2051
2052        let class_id = find_class_by_name(parsed_policy.classes(), "class_two_nlmsg_ranges")
2053            .expect("look up class_two_nlmsg_ranges")
2054            .id();
2055
2056        let rules: Vec<_> = parsed_policy
2057            .access_vector_rules_for_test()
2058            .filter(|rule| rule.metadata.target_class() == class_id)
2059            .collect();
2060
2061        assert_eq!(rules.len(), 2);
2062        assert!(rules[0].metadata.is_allowxperm());
2063        if let Some(xperms) = rules[0].extended_permissions() {
2064            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2065            assert_eq!(xperms.count(), 0x100);
2066            for i in 0x0100..0x01ff {
2067                assert!(xperms.contains(i), "{i}");
2068            }
2069        } else {
2070            panic!("unexpected permission data type")
2071        }
2072        if let Some(xperms) = rules[1].extended_permissions() {
2073            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2074            assert_eq!(xperms.count(), 0x100);
2075            for i in 0x0..0xff {
2076                assert!(xperms.contains(i), "{i}");
2077            }
2078        } else {
2079            panic!("unexpected permission data type")
2080        }
2081    }
2082
2083    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
2084    // of 3 non-consecutive prefix bytes is represented by 3 `AccessVectorRule`s in the
2085    // compiled policy.
2086    //
2087    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
2088    // are not expected to appear in policies.
2089    #[test]
2090    fn parse_allowxperm_three_separate_nlmsg_ranges() {
2091        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2092        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2093        let parsed_policy = &policy.0;
2094        parsed_policy.validate().expect("validate policy");
2095
2096        let class_id =
2097            find_class_by_name(parsed_policy.classes(), "class_three_separate_nlmsg_ranges")
2098                .expect("look up class_three_separate_nlmsg_ranges")
2099                .id();
2100
2101        let rules: Vec<_> = parsed_policy
2102            .access_vector_rules_for_test()
2103            .filter(|rule| rule.metadata.target_class() == class_id)
2104            .collect();
2105
2106        assert_eq!(rules.len(), 3);
2107        assert!(rules[0].metadata.is_allowxperm());
2108        if let Some(xperms) = rules[0].extended_permissions() {
2109            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2110            assert_eq!(xperms.count(), 0x100);
2111            for i in 0x2000..0x20ff {
2112                assert!(xperms.contains(i), "{i}");
2113            }
2114        } else {
2115            panic!("unexpected permission data type")
2116        }
2117        if let Some(xperms) = rules[1].extended_permissions() {
2118            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2119            assert_eq!(xperms.count(), 0x100);
2120            for i in 0x1000..0x10ff {
2121                assert!(xperms.contains(i), "{i}");
2122            }
2123        } else {
2124            panic!("unexpected permission data type")
2125        }
2126        if let Some(xperms) = rules[2].extended_permissions() {
2127            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2128            assert_eq!(xperms.count(), 0x100);
2129            for i in 0x0..0xff {
2130                assert!(xperms.contains(i), "{i}");
2131            }
2132        } else {
2133            panic!("unexpected permission data type")
2134        }
2135    }
2136
2137    // A set of `nlmsg` extended permissions consisting of all 16-bit integers with one
2138    // of 3 (or more) consecutive prefix bytes is represented by 2 `AccessVectorRule`s in the
2139    // compiled policy, one for the smallest prefix byte and one for the largest.
2140    //
2141    // The policy compiler allows `nlmsg` extended permission sets of this form, but they
2142    // are not expected to appear in policies.
2143    #[test]
2144    fn parse_allowxperm_three_contiguous_nlmsg_ranges() {
2145        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2146        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2147        let parsed_policy = &policy.0;
2148        parsed_policy.validate().expect("validate policy");
2149
2150        let class_id =
2151            find_class_by_name(parsed_policy.classes(), "class_three_contiguous_nlmsg_ranges")
2152                .expect("look up class_three_contiguous_nlmsg_ranges")
2153                .id();
2154
2155        let rules: Vec<_> = parsed_policy
2156            .access_vector_rules_for_test()
2157            .filter(|rule| rule.metadata.target_class() == class_id)
2158            .collect();
2159
2160        assert_eq!(rules.len(), 2);
2161        assert!(rules[0].metadata.is_allowxperm());
2162        if let Some(xperms) = rules[0].extended_permissions() {
2163            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2164            assert_eq!(xperms.count(), 0x100);
2165            for i in 0x0200..0x02ff {
2166                assert!(xperms.contains(i), "{i}");
2167            }
2168        } else {
2169            panic!("unexpected permission data type")
2170        }
2171        if let Some(xperms) = rules[1].extended_permissions() {
2172            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2173            assert_eq!(xperms.count(), 0x100);
2174            for i in 0x0..0xff {
2175                assert!(xperms.contains(i), "{i}");
2176            }
2177        } else {
2178            panic!("unexpected permission data type")
2179        }
2180    }
2181
2182    // The representation of extended permissions for `auditallowxperm` rules is
2183    // the same as for `allowxperm` rules.
2184    #[test]
2185    fn parse_auditallowxperm() {
2186        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2187        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2188        let parsed_policy = &policy.0;
2189        parsed_policy.validate().expect("validate policy");
2190
2191        let class_id = find_class_by_name(parsed_policy.classes(), "class_auditallowxperm")
2192            .expect("look up class_auditallowxperm")
2193            .id();
2194
2195        let rules: Vec<_> = parsed_policy
2196            .access_vector_rules_for_test()
2197            .filter(|rule| rule.metadata.target_class() == class_id)
2198            .collect();
2199
2200        assert_eq!(rules.len(), 2);
2201        assert!(rules[0].metadata.is_auditallowxperm());
2202        if let Some(xperms) = rules[0].extended_permissions() {
2203            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2204            assert_eq!(xperms.count(), 1);
2205            assert!(xperms.contains(0x10));
2206        } else {
2207            panic!("unexpected permission data type")
2208        }
2209        if let Some(xperms) = rules[1].extended_permissions() {
2210            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
2211            assert_eq!(xperms.count(), 1);
2212            assert!(xperms.contains(0x1000));
2213        } else {
2214            panic!("unexpected permission data type")
2215        }
2216    }
2217
2218    // The representation of extended permissions for `dontauditxperm` rules is
2219    // the same as for `allowxperm` rules. In particular, the `AccessVectorRule`
2220    // contains the same set of extended permissions that appears in the text
2221    // policy. (This differs from the representation of the access vector in
2222    // `AccessVectorRule`s for `dontaudit` rules, where the `AccessVectorRule`
2223    // contains the complement of the access vector that appears in the text
2224    // policy.)
2225    #[test]
2226    fn parse_dontauditxperm() {
2227        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2228        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2229        let parsed_policy = &policy.0;
2230        parsed_policy.validate().expect("validate policy");
2231
2232        let class_id = find_class_by_name(parsed_policy.classes(), "class_dontauditxperm")
2233            .expect("look up class_dontauditxperm")
2234            .id();
2235
2236        let rules: Vec<_> = parsed_policy
2237            .access_vector_rules_for_test()
2238            .filter(|rule| rule.metadata.target_class() == class_id)
2239            .collect();
2240
2241        assert_eq!(rules.len(), 2);
2242        assert!(rules[0].metadata.is_dontauditxperm());
2243        if let Some(xperms) = rules[0].extended_permissions() {
2244            assert_eq!(xperms.xperms_type, XPERMS_TYPE_NLMSG);
2245            assert_eq!(xperms.count(), 1);
2246            assert!(xperms.contains(0x11));
2247        } else {
2248            panic!("unexpected permission data type")
2249        }
2250        if let Some(xperms) = rules[1].extended_permissions() {
2251            assert_eq!(xperms.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
2252            assert_eq!(xperms.count(), 1);
2253            assert!(xperms.contains(0x1000));
2254        } else {
2255            panic!("unexpected permission data type")
2256        }
2257    }
2258
2259    // If an allowxperm rule and an auditallowxperm rule specify exactly the same permissions, they
2260    // are not coalesced into a single `AccessVectorRule` in the policy; two rules appear in the
2261    // policy.
2262    #[test]
2263    fn parse_auditallowxperm_not_coalesced() {
2264        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
2265        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2266        let parsed_policy = &policy.0;
2267        parsed_policy.validate().expect("validate policy");
2268
2269        let class_id =
2270            find_class_by_name(parsed_policy.classes(), "class_auditallowxperm_not_coalesced")
2271                .expect("class_auditallowxperm_not_coalesced")
2272                .id();
2273
2274        let rules: Vec<_> = parsed_policy
2275            .access_vector_rules_for_test()
2276            .filter(|rule| rule.metadata.target_class() == class_id)
2277            .collect();
2278
2279        assert_eq!(rules.len(), 2);
2280        assert!(rules[0].metadata.is_allowxperm());
2281        assert!(!rules[0].metadata.is_auditallowxperm());
2282        if let Some(xperms) = rules[0].extended_permissions() {
2283            assert_eq!(xperms.count(), 1);
2284            assert!(xperms.contains(0xabcd));
2285        } else {
2286            panic!("unexpected permission data type")
2287        }
2288        assert!(!rules[1].metadata.is_allowxperm());
2289        assert!(rules[1].metadata.is_auditallowxperm());
2290        if let Some(xperms) = rules[1].extended_permissions() {
2291            assert_eq!(xperms.count(), 1);
2292            assert!(xperms.contains(0xabcd));
2293        } else {
2294            panic!("unexpected permission data type")
2295        }
2296    }
2297}