selinux/policy/
arrays.rs

1// Copyright 2023 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
5//! Special cases of `Array<Bytes, Metadata, Data>` and instances of `Metadata` and `Data` that
6//! appear in binary SELinux policies.
7
8use super::error::{ParseError, ValidateError};
9use super::extensible_bitmap::ExtensibleBitmap;
10use super::parser::ParseStrategy;
11use super::symbols::{MlsLevel, MlsRange};
12use super::{
13    array_type, array_type_validate_deref_both, AccessVector, Array, ClassId, Counted, Parse,
14    RoleId, TypeId, UserId, Validate, ValidateArray,
15};
16
17use anyhow::Context as _;
18use std::fmt::Debug;
19use std::num::NonZeroU32;
20use std::ops::Shl;
21use zerocopy::{little_endian as le, FromBytes, Immutable, KnownLayout, Unaligned};
22
23pub(super) const MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY: u32 = 31;
24
25/// Mask for [`AccessVectorRuleMetadata`]'s `access_vector_rule_type` that
26/// indicates that the access vector rule's associated data is a type ID.
27pub(super) const ACCESS_VECTOR_RULE_DATA_IS_TYPE_ID_MASK: u16 = 0x070;
28/// Mask for [`AccessVectorRuleMetadata`]'s `access_vector_rule_type` that
29/// indicates that the access vector rule's associated data is an extended
30/// permission.
31pub(super) const ACCESS_VECTOR_RULE_DATA_IS_XPERM_MASK: u16 = 0x0700;
32
33/// ** Access vector rule types ***
34///
35/// Although these values each have a single bit set, they appear to be
36/// used as enum values rather than as bit masks: i.e., the policy compiler
37/// does not produce access vector rule structures that have more than
38/// one of these types.
39/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
40/// indicates that the access vector rule comes from an `allow [source]
41/// [target]:[class] { [permissions] };` policy statement.
42pub(super) const ACCESS_VECTOR_RULE_TYPE_ALLOW: u16 = 0x1;
43/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
44/// indicates that the access vector rule comes from an `auditallow [source]
45/// [target]:[class] { [permissions] };` policy statement.
46pub(super) const ACCESS_VECTOR_RULE_TYPE_AUDITALLOW: u16 = 0x2;
47/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
48/// indicates that the access vector rule comes from a `dontaudit [source]
49/// [target]:[class] { [permissions] };` policy statement.
50pub(super) const ACCESS_VECTOR_RULE_TYPE_DONTAUDIT: u16 = 0x4;
51/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
52/// indicates that the access vector rule comes from a `type_transition
53/// [source] [target]:[class] [new_type];` policy statement.
54pub(super) const ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION: u16 = 0x10;
55/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
56/// indicates that the access vector rule comes from a `type_member
57/// [source] [target]:[class] [member_type];` policy statement.
58#[allow(dead_code)]
59pub(super) const ACCESS_VECTOR_RULE_TYPE_TYPE_MEMBER: u16 = 0x20;
60/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type` that
61/// indicates that the access vector rule comes from a `type_change
62/// [source] [target]:[class] [change_type];` policy statement.
63#[allow(dead_code)]
64pub(super) const ACCESS_VECTOR_RULE_TYPE_TYPE_CHANGE: u16 = 0x40;
65/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type`
66/// that indicates that the access vector rule comes from an
67/// `allowxperm [source] [target]:[class] [permission] {
68/// [extended_permissions] };` policy statement.
69pub(super) const ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM: u16 = 0x100;
70/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type`
71/// that indicates that the access vector rule comes from an
72/// `auditallowxperm [source] [target]:[class] [permission] {
73/// [extended_permissions] };` policy statement.
74pub(super) const ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM: u16 = 0x200;
75/// Value for [`AccessVectorRuleMetadata`] `access_vector_rule_type`
76/// that indicates that the access vector rule comes from an
77/// `dontauditxperm [source] [target]:[class] [permission] {
78/// [extended_permissions] };` policy statement.
79pub(super) const ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM: u16 = 0x400;
80
81/// ** Extended permissions types ***
82///
83/// Value for ['ExtendedPermissions'] `xperms_type` that indicates
84/// that the xperms set is a proper subset of the 16-bit ioctl
85/// xperms with a given high byte value.
86pub(super) const XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES: u8 = 1;
87/// Value for ['ExtendedPermissions'] `xperms_type` that indicates
88/// that the xperms set consists of all 16-bit ioctl xperms with a
89/// given high byte, for one or more high byte values.
90pub(super) const XPERMS_TYPE_IOCTL_PREFIXES: u8 = 2;
91
92#[allow(type_alias_bounds)]
93pub(super) type SimpleArray<PS: ParseStrategy, T> = Array<PS, PS::Output<le::U32>, T>;
94
95impl<PS: ParseStrategy, T: Validate> Validate for SimpleArray<PS, T> {
96    type Error = <T as Validate>::Error;
97
98    /// Defers to `self.data` for validation. `self.data` has access to all information, including
99    /// size stored in `self.metadata`.
100    fn validate(&self) -> Result<(), Self::Error> {
101        self.data.validate()
102    }
103}
104
105impl Counted for le::U32 {
106    fn count(&self) -> u32 {
107        self.get()
108    }
109}
110
111pub(super) type ConditionalNodes<PS> = Vec<ConditionalNode<PS>>;
112
113impl<PS: ParseStrategy> Validate for ConditionalNodes<PS> {
114    type Error = anyhow::Error;
115
116    /// TODO: Validate internal consistency between consecutive [`ConditionalNode`] instances.
117    fn validate(&self) -> Result<(), Self::Error> {
118        Ok(())
119    }
120}
121
122array_type!(
123    ConditionalNodeItems,
124    PS,
125    PS::Output<ConditionalNodeMetadata>,
126    PS::Slice<ConditionalNodeDatum>
127);
128
129array_type_validate_deref_both!(ConditionalNodeItems);
130
131impl<PS: ParseStrategy> ValidateArray<ConditionalNodeMetadata, ConditionalNodeDatum>
132    for ConditionalNodeItems<PS>
133{
134    type Error = anyhow::Error;
135
136    /// TODO: Validate internal consistency between [`ConditionalNodeMetadata`] consecutive
137    /// [`ConditionalNodeDatum`].
138    fn validate_array<'a>(
139        _metadata: &'a ConditionalNodeMetadata,
140        _data: &'a [ConditionalNodeDatum],
141    ) -> Result<(), Self::Error> {
142        Ok(())
143    }
144}
145
146#[derive(Debug, PartialEq)]
147pub(super) struct ConditionalNode<PS: ParseStrategy> {
148    items: ConditionalNodeItems<PS>,
149    true_list: SimpleArray<PS, AccessVectorRules<PS>>,
150    false_list: SimpleArray<PS, AccessVectorRules<PS>>,
151}
152
153impl<PS: ParseStrategy> Parse<PS> for ConditionalNode<PS>
154where
155    ConditionalNodeItems<PS>: Parse<PS>,
156    SimpleArray<PS, AccessVectorRules<PS>>: Parse<PS>,
157{
158    type Error = anyhow::Error;
159
160    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
161        let tail = bytes;
162
163        let (items, tail) = ConditionalNodeItems::parse(tail)
164            .map_err(Into::<anyhow::Error>::into)
165            .context("parsing conditional node items")?;
166
167        let (true_list, tail) = SimpleArray::<PS, AccessVectorRules<PS>>::parse(tail)
168            .map_err(Into::<anyhow::Error>::into)
169            .context("parsing conditional node true list")?;
170
171        let (false_list, tail) = SimpleArray::<PS, AccessVectorRules<PS>>::parse(tail)
172            .map_err(Into::<anyhow::Error>::into)
173            .context("parsing conditional node false list")?;
174
175        Ok((Self { items, true_list, false_list }, tail))
176    }
177}
178
179#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
180#[repr(C, packed)]
181pub(super) struct ConditionalNodeMetadata {
182    state: le::U32,
183    count: le::U32,
184}
185
186impl Counted for ConditionalNodeMetadata {
187    fn count(&self) -> u32 {
188        self.count.get()
189    }
190}
191
192impl Validate for ConditionalNodeMetadata {
193    type Error = anyhow::Error;
194
195    /// TODO: Validate [`ConditionalNodeMetadata`] internals.
196    fn validate(&self) -> Result<(), Self::Error> {
197        Ok(())
198    }
199}
200
201#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
202#[repr(C, packed)]
203pub(super) struct ConditionalNodeDatum {
204    node_type: le::U32,
205    boolean: le::U32,
206}
207
208impl Validate for [ConditionalNodeDatum] {
209    type Error = anyhow::Error;
210
211    /// TODO: Validate sequence of [`ConditionalNodeDatum`].
212    fn validate(&self) -> Result<(), Self::Error> {
213        Ok(())
214    }
215}
216
217/// The list of access control rules defined by policy statements of the
218/// following kinds:
219/// - `allow`, `dontaudit`, `auditallow`, and `neverallow`, which specify
220///   an access vector describing a permission set.
221/// - `allowxperm`, `auditallowxperm`, `dontaudit`, which specify a set
222///   of extended permissions.
223/// - `type_transition`, `type_change`, and `type_member', which include
224///   a type id describing a permitted new type.
225pub(super) type AccessVectorRules<PS> = Vec<AccessVectorRule<PS>>;
226
227impl<PS: ParseStrategy> Validate for AccessVectorRules<PS> {
228    type Error = anyhow::Error;
229
230    fn validate(&self) -> Result<(), Self::Error> {
231        for access_vector_rule in self {
232            access_vector_rule.validate()?;
233        }
234        Ok(())
235    }
236}
237
238#[derive(Debug, PartialEq)]
239pub(super) struct AccessVectorRule<PS: ParseStrategy> {
240    pub metadata: PS::Output<AccessVectorRuleMetadata>,
241    permission_data: PermissionData<PS>,
242}
243
244impl<PS: ParseStrategy> AccessVectorRule<PS> {
245    /// Returns whether this access vector rule comes from an
246    /// `allow [source] [target]:[class] { [permissions] };` policy statement.
247    pub fn is_allow(&self) -> bool {
248        (PS::deref(&self.metadata).access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_ALLOW) != 0
249    }
250
251    /// Returns whether this access vector rule comes from an
252    /// `auditallow [source] [target]:[class] { [permissions] };` policy statement.
253    pub fn is_auditallow(&self) -> bool {
254        (PS::deref(&self.metadata).access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_AUDITALLOW)
255            != 0
256    }
257
258    /// Returns whether this access vector rule comes from an
259    /// `dontaudit [source] [target]:[class] { [permissions] };` policy statement.
260    pub fn is_dontaudit(&self) -> bool {
261        (PS::deref(&self.metadata).access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_DONTAUDIT) != 0
262    }
263
264    /// Returns whether this access vector rule comes from a
265    /// `type_transition [source] [target]:[class] [new_type];` policy statement.
266    pub fn is_type_transition(&self) -> bool {
267        (PS::deref(&self.metadata).access_vector_rule_type
268            & ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION)
269            != 0
270    }
271
272    /// Returns whether this access vector rule comes from an
273    /// `allowxperm [source] [target]:[class] [permission] {
274    /// [extended_permissions] };` policy statement.
275    #[allow(dead_code)]
276    pub fn is_allowxperm(&self) -> bool {
277        (PS::deref(&self.metadata).access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM)
278            != 0
279    }
280
281    /// Returns whether this access vector rule comes from an
282    /// `auditallowxperm [source] [target]:[class] [permission] {
283    /// [extended_permissions] };` policy statement.
284    #[allow(dead_code)]
285    pub fn is_auditallowxperm(&self) -> bool {
286        (PS::deref(&self.metadata).access_vector_rule_type
287            & ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM)
288            != 0
289    }
290
291    /// Returns whether this access vector rule comes from a
292    /// `dontauditxperm [source] [target]:[class] [permission] {
293    /// [extended_permissions] };` policy statement.
294    #[allow(dead_code)]
295    pub fn is_dontauditxperm(&self) -> bool {
296        (PS::deref(&self.metadata).access_vector_rule_type & ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM)
297            != 0
298    }
299
300    /// Returns the source type id in this access vector rule. This id
301    /// corresponds to the [`super::symbols::Type`] `id()` of some type or
302    /// attribute in the same policy.
303    pub fn source_type(&self) -> TypeId {
304        TypeId(NonZeroU32::new(PS::deref(&self.metadata).source_type.into()).unwrap())
305    }
306
307    /// Returns the target type id in this access vector rule. This id
308    /// corresponds to the [`super::symbols::Type`] `id()` of some type or
309    /// attribute in the same policy.
310    pub fn target_type(&self) -> TypeId {
311        TypeId(NonZeroU32::new(PS::deref(&self.metadata).target_type.into()).unwrap())
312    }
313
314    /// Returns the target class id in this access vector rule. This id
315    /// corresponds to the [`super::symbols::Class`] `id()` of some class in the
316    /// same policy. Although the index is returned as a 32-bit value, the field
317    /// itself is 16-bit
318    pub fn target_class(&self) -> ClassId {
319        ClassId(NonZeroU32::new(PS::deref(&self.metadata).class.into()).unwrap())
320    }
321
322    /// An access vector that corresponds to the `[access_vector]` in an
323    /// `allow [source] [target]:[class] [access_vector]` policy statement,
324    /// or similarly for an `auditallow` or `dontaudit` policy statement.
325    /// Return value is `None` if this access vector rule corresponds to a
326    /// different kind of policy statement.
327    pub fn access_vector(&self) -> Option<AccessVector> {
328        match &self.permission_data {
329            PermissionData::AccessVector(access_vector_raw) => {
330                Some(AccessVector((*PS::deref(access_vector_raw)).get()))
331            }
332            _ => None,
333        }
334    }
335
336    /// A numeric type id that corresponds to the `[new_type]` in a
337    /// `type_transition [source] [target]:[class] [new_type];` policy statement,
338    /// or similarly for a `type_member` or `type_change` policy statement.
339    /// Return value is `None` if this access vector rule corresponds to a
340    /// different kind of policy statement.
341    pub fn new_type(&self) -> Option<TypeId> {
342        match &self.permission_data {
343            PermissionData::NewType(new_type) => {
344                Some(TypeId(NonZeroU32::new(PS::deref(new_type).get().into()).unwrap()))
345            }
346            _ => None,
347        }
348    }
349
350    /// A set of extended permissions that corresponds to the `[xperms]` in an
351    /// `allowxperm [source][target]:[class] [permission] [xperms]` policy
352    /// statement, or similarly for an `auditallowxperm` or `dontauditxperm`
353    /// policy statement. Return value is `None` if this access vector rule
354    /// corresponds to a different kind of policy statement.
355    #[allow(dead_code)]
356    pub fn extended_permissions(&self) -> Option<&ExtendedPermissions> {
357        match &self.permission_data {
358            PermissionData::ExtendedPermissions(xperms) => Some(xperms),
359            _ => None,
360        }
361    }
362}
363
364impl<PS: ParseStrategy> Parse<PS> for AccessVectorRule<PS> {
365    type Error = anyhow::Error;
366
367    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
368        let tail = bytes;
369
370        let num_bytes = tail.len();
371        let (metadata, tail) =
372            PS::parse::<AccessVectorRuleMetadata>(tail).ok_or(ParseError::MissingData {
373                type_name: std::any::type_name::<AccessVectorRuleMetadata>(),
374                type_size: std::mem::size_of::<AccessVectorRuleMetadata>(),
375                num_bytes,
376            })?;
377        let access_vector_rule_type = PS::deref(&metadata).access_vector_rule_type;
378        let num_bytes = tail.len();
379        let (permission_data, tail) =
380            if (access_vector_rule_type & ACCESS_VECTOR_RULE_DATA_IS_XPERM_MASK) != 0 {
381                let (xperms, tail) = ExtendedPermissions::parse(tail)
382                    .map_err(Into::<anyhow::Error>::into)
383                    .context("parsing extended permissions")?;
384                (PermissionData::ExtendedPermissions(xperms), tail)
385            } else if (access_vector_rule_type & ACCESS_VECTOR_RULE_DATA_IS_TYPE_ID_MASK) != 0 {
386                let (new_type, tail) =
387                    PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
388                        type_name: "PermissionData::NewType",
389                        type_size: std::mem::size_of::<le::U32>(),
390                        num_bytes,
391                    })?;
392                (PermissionData::NewType(new_type), tail)
393            } else {
394                let (access_vector, tail) =
395                    PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
396                        type_name: "PermissionData::AccessVector",
397                        type_size: std::mem::size_of::<le::U32>(),
398                        num_bytes,
399                    })?;
400                (PermissionData::AccessVector(access_vector), tail)
401            };
402        Ok((Self { metadata, permission_data }, tail))
403    }
404}
405
406impl<PS: ParseStrategy> Validate for AccessVectorRule<PS> {
407    type Error = anyhow::Error;
408
409    fn validate(&self) -> Result<(), Self::Error> {
410        if PS::deref(&self.metadata).class.get() == 0 {
411            return Err(ValidateError::NonOptionalIdIsZero.into());
412        }
413        if let PermissionData::ExtendedPermissions(xperms) = &self.permission_data {
414            let xperms_type = xperms.xperms_type;
415            if !(xperms_type == XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES
416                || xperms_type == XPERMS_TYPE_IOCTL_PREFIXES)
417            {
418                return Err(
419                    ValidateError::InvalidExtendedPermissionsType { type_: xperms_type }.into()
420                );
421            }
422        }
423        Ok(())
424    }
425}
426
427#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
428#[repr(C, packed)]
429pub(super) struct AccessVectorRuleMetadata {
430    source_type: le::U16,
431    target_type: le::U16,
432    class: le::U16,
433    access_vector_rule_type: le::U16,
434}
435
436#[derive(Debug, PartialEq)]
437pub(super) enum PermissionData<PS: ParseStrategy> {
438    AccessVector(PS::Output<le::U32>),
439    NewType(PS::Output<le::U32>),
440    ExtendedPermissions(ExtendedPermissions),
441}
442
443#[derive(Clone, Debug, PartialEq)]
444pub(super) struct ExtendedPermissions {
445    pub(super) xperms_type: u8,
446    pub(super) xperms_optional_prefix: u8,
447    pub(super) xperms_bitmap: XpermsBitmap,
448}
449
450impl<PS: ParseStrategy> Parse<PS> for ExtendedPermissions {
451    type Error = anyhow::Error;
452
453    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
454        let tail = bytes;
455        let num_bytes = tail.len();
456        let (type_, tail) = PS::parse::<u8>(tail).ok_or(ParseError::MissingData {
457            type_name: "ExtendedPermissions::xperms_type",
458            type_size: std::mem::size_of::<u8>(),
459            num_bytes,
460        })?;
461        let xperms_type = *PS::deref(&type_);
462        let num_bytes = tail.len();
463        let (prefix, tail) = PS::parse::<u8>(tail).ok_or(ParseError::MissingData {
464            type_name: "ExtendedPermissions::xperms_optional_prefix",
465            type_size: std::mem::size_of::<u8>(),
466            num_bytes,
467        })?;
468        let xperms_optional_prefix = *PS::deref(&prefix);
469        let num_bytes = tail.len();
470        let (bitmap, tail) = PS::parse::<[le::U32; 8]>(tail).ok_or(ParseError::MissingData {
471            type_name: "ExtendedPermissions::xperms_bitmap",
472            type_size: std::mem::size_of::<[le::U32; 8]>(),
473            num_bytes,
474        })?;
475        Ok((
476            ExtendedPermissions {
477                xperms_type,
478                xperms_optional_prefix,
479                xperms_bitmap: XpermsBitmap(*PS::deref(&bitmap)),
480            },
481            tail,
482        ))
483    }
484}
485
486impl ExtendedPermissions {
487    #[cfg(test)]
488    fn count(&self) -> u64 {
489        let count = self
490            .xperms_bitmap
491            .0
492            .iter()
493            .fold(0, |count, block| (count as u64) + (block.get().count_ones() as u64));
494        match self.xperms_type {
495            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES => count,
496            XPERMS_TYPE_IOCTL_PREFIXES => count * 0x100,
497            _ => unreachable!("invalid xperms_type in validated ExtendedPermissions"),
498        }
499    }
500
501    #[cfg(test)]
502    fn contains(&self, xperm: u16) -> bool {
503        let [postfix, prefix] = xperm.to_le_bytes();
504        if self.xperms_type == XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES
505            && self.xperms_optional_prefix != prefix
506        {
507            return false;
508        }
509        let value = match self.xperms_type {
510            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES => postfix,
511            XPERMS_TYPE_IOCTL_PREFIXES => prefix,
512            _ => unreachable!("invalid xperms_type in validated ExtendedPermissions"),
513        };
514        self.xperms_bitmap.contains(value)
515    }
516}
517
518// A bitmap representing a subset of `{0x0,...,0xff}`.
519#[derive(Clone, Debug, PartialEq)]
520pub struct XpermsBitmap([le::U32; 8]);
521
522impl XpermsBitmap {
523    const BITMAP_BLOCKS: usize = 8;
524    pub const ALL: Self = Self([le::U32::MAX_VALUE; Self::BITMAP_BLOCKS]);
525    pub const NONE: Self = Self([le::U32::ZERO; Self::BITMAP_BLOCKS]);
526
527    #[cfg(test)]
528    pub fn new(elements: [le::U32; 8]) -> Self {
529        Self(elements)
530    }
531
532    pub fn contains(&self, value: u8) -> bool {
533        let block_index = (value as usize) / 32;
534        let bit_index = ((value as usize) % 32) as u32;
535        self.0[block_index] & le::U32::new(1).shl(bit_index) != 0
536    }
537}
538
539impl std::ops::BitOrAssign<&Self> for XpermsBitmap {
540    fn bitor_assign(&mut self, rhs: &Self) {
541        (0..Self::BITMAP_BLOCKS).for_each(|i| self.0[i] |= rhs.0[i])
542    }
543}
544
545impl std::ops::SubAssign<&Self> for XpermsBitmap {
546    fn sub_assign(&mut self, rhs: &Self) {
547        (0..Self::BITMAP_BLOCKS).for_each(|i| self.0[i] = self.0[i] ^ (self.0[i] & rhs.0[i]))
548    }
549}
550
551array_type!(RoleTransitions, PS, PS::Output<le::U32>, PS::Slice<RoleTransition>);
552
553array_type_validate_deref_both!(RoleTransitions);
554
555impl<PS: ParseStrategy> ValidateArray<le::U32, RoleTransition> for RoleTransitions<PS> {
556    type Error = anyhow::Error;
557
558    /// [`RoleTransitions`] have no additional metadata (beyond length encoding).
559    fn validate_array<'a>(
560        _metadata: &'a le::U32,
561        _data: &'a [RoleTransition],
562    ) -> Result<(), Self::Error> {
563        _data.validate()
564    }
565}
566
567#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
568#[repr(C, packed)]
569pub(super) struct RoleTransition {
570    role: le::U32,
571    role_type: le::U32,
572    new_role: le::U32,
573    tclass: le::U32,
574}
575
576impl RoleTransition {
577    pub(super) fn current_role(&self) -> RoleId {
578        RoleId(NonZeroU32::new(self.role.get()).unwrap())
579    }
580
581    pub(super) fn type_(&self) -> TypeId {
582        TypeId(NonZeroU32::new(self.role_type.get()).unwrap())
583    }
584
585    pub(super) fn class(&self) -> ClassId {
586        ClassId(NonZeroU32::new(self.tclass.get()).unwrap())
587    }
588
589    pub(super) fn new_role(&self) -> RoleId {
590        RoleId(NonZeroU32::new(self.new_role.get()).unwrap())
591    }
592}
593
594impl Validate for [RoleTransition] {
595    type Error = anyhow::Error;
596
597    fn validate(&self) -> Result<(), Self::Error> {
598        for role_transition in self {
599            if role_transition.tclass.get() == 0 {
600                return Err(ValidateError::NonOptionalIdIsZero.into());
601            }
602        }
603        Ok(())
604    }
605}
606
607array_type!(RoleAllows, PS, PS::Output<le::U32>, PS::Slice<RoleAllow>);
608
609array_type_validate_deref_both!(RoleAllows);
610
611impl<PS: ParseStrategy> ValidateArray<le::U32, RoleAllow> for RoleAllows<PS> {
612    type Error = anyhow::Error;
613
614    /// [`RoleAllows`] have no additional metadata (beyond length encoding).
615    fn validate_array<'a>(
616        _metadata: &'a le::U32,
617        _data: &'a [RoleAllow],
618    ) -> Result<(), Self::Error> {
619        Ok(())
620    }
621}
622
623#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
624#[repr(C, packed)]
625pub(super) struct RoleAllow {
626    role: le::U32,
627    new_role: le::U32,
628}
629
630impl RoleAllow {
631    #[allow(dead_code)]
632    // TODO(http://b/334968228): fn to be used again when checking role allow rules separately from
633    // SID calculation.
634    pub(super) fn source_role(&self) -> RoleId {
635        RoleId(NonZeroU32::new(self.role.get()).unwrap())
636    }
637
638    #[allow(dead_code)]
639    // TODO(http://b/334968228): fn to be used again when checking role allow rules separately from
640    // SID calculation.
641    pub(super) fn new_role(&self) -> RoleId {
642        RoleId(NonZeroU32::new(self.new_role.get()).unwrap())
643    }
644}
645
646impl Validate for [RoleAllow] {
647    type Error = anyhow::Error;
648
649    /// TODO: Validate sequence of [`RoleAllow`].
650    fn validate(&self) -> Result<(), Self::Error> {
651        Ok(())
652    }
653}
654
655#[derive(Debug, PartialEq)]
656pub(super) enum FilenameTransitionList<PS: ParseStrategy> {
657    PolicyVersionGeq33(SimpleArray<PS, FilenameTransitions<PS>>),
658    PolicyVersionLeq32(SimpleArray<PS, DeprecatedFilenameTransitions<PS>>),
659}
660
661impl<PS: ParseStrategy> Validate for FilenameTransitionList<PS> {
662    type Error = anyhow::Error;
663
664    fn validate(&self) -> Result<(), Self::Error> {
665        match self {
666            Self::PolicyVersionLeq32(list) => list.validate().map_err(Into::<anyhow::Error>::into),
667            Self::PolicyVersionGeq33(list) => list.validate().map_err(Into::<anyhow::Error>::into),
668        }
669    }
670}
671
672pub(super) type FilenameTransitions<PS> = Vec<FilenameTransition<PS>>;
673
674impl<PS: ParseStrategy> Validate for FilenameTransitions<PS> {
675    type Error = anyhow::Error;
676
677    /// TODO: Validate sequence of [`FilenameTransition`] objects.
678    fn validate(&self) -> Result<(), Self::Error> {
679        Ok(())
680    }
681}
682
683#[derive(Debug, PartialEq)]
684pub(super) struct FilenameTransition<PS: ParseStrategy> {
685    filename: SimpleArray<PS, PS::Slice<u8>>,
686    transition_type: PS::Output<le::U32>,
687    transition_class: PS::Output<le::U32>,
688    items: SimpleArray<PS, FilenameTransitionItems<PS>>,
689}
690
691impl<PS: ParseStrategy> FilenameTransition<PS> {
692    pub(super) fn name_bytes(&self) -> &[u8] {
693        PS::deref_slice(&self.filename.data)
694    }
695
696    pub(super) fn target_type(&self) -> TypeId {
697        TypeId(NonZeroU32::new(PS::deref(&self.transition_type).get()).unwrap())
698    }
699
700    pub(super) fn target_class(&self) -> ClassId {
701        ClassId(NonZeroU32::new(PS::deref(&self.transition_class).get()).unwrap())
702    }
703
704    pub(super) fn outputs(&self) -> &[FilenameTransitionItem<PS>] {
705        &self.items.data
706    }
707}
708
709impl<PS: ParseStrategy> Parse<PS> for FilenameTransition<PS>
710where
711    SimpleArray<PS, PS::Slice<u8>>: Parse<PS>,
712    SimpleArray<PS, FilenameTransitionItems<PS>>: Parse<PS>,
713{
714    type Error = anyhow::Error;
715
716    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
717        let tail = bytes;
718
719        let (filename, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail)
720            .map_err(Into::<anyhow::Error>::into)
721            .context("parsing filename for filename transition")?;
722
723        let num_bytes = tail.len();
724        let (transition_type, tail) =
725            PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
726                type_name: "FilenameTransition::transition_type",
727                type_size: std::mem::size_of::<le::U32>(),
728                num_bytes,
729            })?;
730
731        let num_bytes = tail.len();
732        let (transition_class, tail) =
733            PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
734                type_name: "FilenameTransition::transition_class",
735                type_size: std::mem::size_of::<le::U32>(),
736                num_bytes,
737            })?;
738
739        let (items, tail) = SimpleArray::<PS, FilenameTransitionItems<PS>>::parse(tail)
740            .map_err(Into::<anyhow::Error>::into)
741            .context("parsing items for filename transition")?;
742
743        Ok((Self { filename, transition_type, transition_class, items }, tail))
744    }
745}
746
747pub(super) type FilenameTransitionItems<PS> = Vec<FilenameTransitionItem<PS>>;
748
749#[derive(Debug, PartialEq)]
750pub(super) struct FilenameTransitionItem<PS: ParseStrategy> {
751    stypes: ExtensibleBitmap<PS>,
752    out_type: PS::Output<le::U32>,
753}
754
755impl<PS: ParseStrategy> FilenameTransitionItem<PS> {
756    pub(super) fn has_source_type(&self, source_type: TypeId) -> bool {
757        self.stypes.is_set(source_type.0.get() - 1)
758    }
759
760    pub(super) fn out_type(&self) -> TypeId {
761        TypeId(NonZeroU32::new(PS::deref(&self.out_type).get()).unwrap())
762    }
763}
764
765impl<PS: ParseStrategy> Parse<PS> for FilenameTransitionItem<PS>
766where
767    ExtensibleBitmap<PS>: Parse<PS>,
768{
769    type Error = anyhow::Error;
770
771    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
772        let tail = bytes;
773
774        let (stypes, tail) = ExtensibleBitmap::parse(tail)
775            .map_err(Into::<anyhow::Error>::into)
776            .context("parsing stypes extensible bitmap for file transition")?;
777
778        let num_bytes = tail.len();
779        let (out_type, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
780            type_name: "FilenameTransitionItem::out_type",
781            type_size: std::mem::size_of::<le::U32>(),
782            num_bytes,
783        })?;
784
785        Ok((Self { stypes, out_type }, tail))
786    }
787}
788
789pub(super) type DeprecatedFilenameTransitions<PS> = Vec<DeprecatedFilenameTransition<PS>>;
790
791impl<PS: ParseStrategy> Validate for DeprecatedFilenameTransitions<PS> {
792    type Error = anyhow::Error;
793
794    /// TODO: Validate sequence of [`DeprecatedFilenameTransition`] objects.
795    fn validate(&self) -> Result<(), Self::Error> {
796        Ok(())
797    }
798}
799
800#[derive(Debug, PartialEq)]
801pub(super) struct DeprecatedFilenameTransition<PS: ParseStrategy> {
802    filename: SimpleArray<PS, PS::Slice<u8>>,
803    metadata: PS::Output<DeprecatedFilenameTransitionMetadata>,
804}
805
806impl<PS: ParseStrategy> DeprecatedFilenameTransition<PS> {
807    pub(super) fn name_bytes(&self) -> &[u8] {
808        PS::deref_slice(&self.filename.data)
809    }
810
811    pub(super) fn source_type(&self) -> TypeId {
812        TypeId(NonZeroU32::new(PS::deref(&self.metadata).source_type.get()).unwrap())
813    }
814
815    pub(super) fn target_type(&self) -> TypeId {
816        TypeId(NonZeroU32::new(PS::deref(&self.metadata).transition_type.get()).unwrap())
817    }
818
819    pub(super) fn target_class(&self) -> ClassId {
820        ClassId(NonZeroU32::new(PS::deref(&self.metadata).transition_class.get()).unwrap())
821    }
822
823    pub(super) fn out_type(&self) -> TypeId {
824        TypeId(NonZeroU32::new(PS::deref(&self.metadata).out_type.get()).unwrap())
825    }
826}
827
828impl<PS: ParseStrategy> Parse<PS> for DeprecatedFilenameTransition<PS>
829where
830    SimpleArray<PS, PS::Slice<u8>>: Parse<PS>,
831{
832    type Error = anyhow::Error;
833
834    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
835        let tail = bytes;
836
837        let (filename, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail)
838            .map_err(Into::<anyhow::Error>::into)
839            .context("parsing filename for deprecated filename transition")?;
840
841        let num_bytes = tail.len();
842        let (metadata, tail) = PS::parse::<DeprecatedFilenameTransitionMetadata>(tail).ok_or(
843            ParseError::MissingData {
844                type_name: "DeprecatedFilenameTransition::metadata",
845                type_size: std::mem::size_of::<le::U32>(),
846                num_bytes,
847            },
848        )?;
849
850        Ok((Self { filename, metadata }, tail))
851    }
852}
853
854#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
855#[repr(C, packed)]
856pub(super) struct DeprecatedFilenameTransitionMetadata {
857    source_type: le::U32,
858    transition_type: le::U32,
859    transition_class: le::U32,
860    out_type: le::U32,
861}
862
863pub(super) type InitialSids<PS> = Vec<InitialSid<PS>>;
864
865impl<PS: ParseStrategy> Validate for InitialSids<PS> {
866    type Error = anyhow::Error;
867
868    /// TODO: Validate consistency of sequence of [`InitialSid`] objects.
869    fn validate(&self) -> Result<(), Self::Error> {
870        for initial_sid in crate::InitialSid::all_variants() {
871            self.iter()
872                .find(|initial| initial.id().get() == initial_sid as u32)
873                .ok_or(ValidateError::MissingInitialSid { initial_sid })?;
874        }
875        Ok(())
876    }
877}
878
879#[derive(Debug, PartialEq)]
880pub(super) struct InitialSid<PS: ParseStrategy> {
881    id: PS::Output<le::U32>,
882    context: Context<PS>,
883}
884
885impl<PS: ParseStrategy> InitialSid<PS> {
886    pub(super) fn id(&self) -> le::U32 {
887        *PS::deref(&self.id)
888    }
889
890    pub(super) fn context(&self) -> &Context<PS> {
891        &self.context
892    }
893}
894
895impl<PS: ParseStrategy> Parse<PS> for InitialSid<PS>
896where
897    Context<PS>: Parse<PS>,
898{
899    type Error = anyhow::Error;
900
901    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
902        let tail = bytes;
903
904        let num_bytes = tail.len();
905        let (id, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
906            type_name: "InitialSid::sid",
907            type_size: std::mem::size_of::<le::U32>(),
908            num_bytes,
909        })?;
910
911        let (context, tail) = Context::parse(tail)
912            .map_err(Into::<anyhow::Error>::into)
913            .context("parsing context for initial sid")?;
914
915        Ok((Self { id, context }, tail))
916    }
917}
918
919#[derive(Debug, PartialEq)]
920pub(super) struct Context<PS: ParseStrategy> {
921    metadata: PS::Output<ContextMetadata>,
922    mls_range: MlsRange<PS>,
923}
924
925impl<PS: ParseStrategy> Context<PS> {
926    pub(super) fn user_id(&self) -> UserId {
927        UserId(NonZeroU32::new(PS::deref(&self.metadata).user.get()).unwrap())
928    }
929    pub(super) fn role_id(&self) -> RoleId {
930        RoleId(NonZeroU32::new(PS::deref(&self.metadata).role.get()).unwrap())
931    }
932    pub(super) fn type_id(&self) -> TypeId {
933        TypeId(NonZeroU32::new(PS::deref(&self.metadata).context_type.get()).unwrap())
934    }
935    pub(super) fn low_level(&self) -> &MlsLevel<PS> {
936        self.mls_range.low()
937    }
938    pub(super) fn high_level(&self) -> &Option<MlsLevel<PS>> {
939        self.mls_range.high()
940    }
941}
942
943impl<PS: ParseStrategy> Parse<PS> for Context<PS>
944where
945    MlsRange<PS>: Parse<PS>,
946{
947    type Error = anyhow::Error;
948
949    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
950        let tail = bytes;
951
952        let (metadata, tail) =
953            PS::parse::<ContextMetadata>(tail).context("parsing metadata for context")?;
954
955        let (mls_range, tail) = MlsRange::parse(tail)
956            .map_err(Into::<anyhow::Error>::into)
957            .context("parsing mls range for context")?;
958
959        Ok((Self { metadata, mls_range }, tail))
960    }
961}
962
963#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
964#[repr(C, packed)]
965pub(super) struct ContextMetadata {
966    user: le::U32,
967    role: le::U32,
968    context_type: le::U32,
969}
970
971pub(super) type NamedContextPairs<PS> = Vec<NamedContextPair<PS>>;
972
973impl<PS: ParseStrategy> Validate for NamedContextPairs<PS> {
974    type Error = anyhow::Error;
975
976    /// TODO: Validate consistency of sequence of [`NamedContextPairs`] objects.
977    ///
978    /// TODO: Is different validation required for `filesystems` and `network_interfaces`? If so,
979    /// create wrapper types with different [`Validate`] implementations.
980    fn validate(&self) -> Result<(), Self::Error> {
981        Ok(())
982    }
983}
984
985#[derive(Debug, PartialEq)]
986pub(super) struct NamedContextPair<PS: ParseStrategy> {
987    name: SimpleArray<PS, PS::Slice<u8>>,
988    context1: Context<PS>,
989    context2: Context<PS>,
990}
991
992impl<PS: ParseStrategy> Parse<PS> for NamedContextPair<PS>
993where
994    SimpleArray<PS, PS::Slice<u8>>: Parse<PS>,
995    Context<PS>: Parse<PS>,
996{
997    type Error = anyhow::Error;
998
999    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1000        let tail = bytes;
1001
1002        let (name, tail) = SimpleArray::parse(tail)
1003            .map_err(Into::<anyhow::Error>::into)
1004            .context("parsing filesystem context name")?;
1005
1006        let (context1, tail) = Context::parse(tail)
1007            .map_err(Into::<anyhow::Error>::into)
1008            .context("parsing first context for filesystem context")?;
1009
1010        let (context2, tail) = Context::parse(tail)
1011            .map_err(Into::<anyhow::Error>::into)
1012            .context("parsing second context for filesystem context")?;
1013
1014        Ok((Self { name, context1, context2 }, tail))
1015    }
1016}
1017
1018pub(super) type Ports<PS> = Vec<Port<PS>>;
1019
1020impl<PS: ParseStrategy> Validate for Ports<PS> {
1021    type Error = anyhow::Error;
1022
1023    /// TODO: Validate consistency of sequence of [`Ports`] objects.
1024    fn validate(&self) -> Result<(), Self::Error> {
1025        Ok(())
1026    }
1027}
1028
1029#[derive(Debug, PartialEq)]
1030pub(super) struct Port<PS: ParseStrategy> {
1031    metadata: PS::Output<PortMetadata>,
1032    context: Context<PS>,
1033}
1034
1035impl<PS: ParseStrategy> Parse<PS> for Port<PS>
1036where
1037    Context<PS>: Parse<PS>,
1038{
1039    type Error = anyhow::Error;
1040
1041    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1042        let tail = bytes;
1043
1044        let (metadata, tail) =
1045            PS::parse::<PortMetadata>(tail).context("parsing metadata for context")?;
1046
1047        let (context, tail) = Context::parse(tail)
1048            .map_err(Into::<anyhow::Error>::into)
1049            .context("parsing context for port")?;
1050
1051        Ok((Self { metadata, context }, tail))
1052    }
1053}
1054
1055#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1056#[repr(C, packed)]
1057pub(super) struct PortMetadata {
1058    protocol: le::U32,
1059    low_port: le::U32,
1060    high_port: le::U32,
1061}
1062
1063pub(super) type Nodes<PS> = Vec<Node<PS>>;
1064
1065impl<PS: ParseStrategy> Validate for Nodes<PS> {
1066    type Error = anyhow::Error;
1067
1068    /// TODO: Validate consistency of sequence of [`Node`] objects.
1069    fn validate(&self) -> Result<(), Self::Error> {
1070        Ok(())
1071    }
1072}
1073
1074#[derive(Debug, PartialEq)]
1075pub(super) struct Node<PS: ParseStrategy> {
1076    address: PS::Output<le::U32>,
1077    mask: PS::Output<le::U32>,
1078    context: Context<PS>,
1079}
1080
1081impl<PS: ParseStrategy> Parse<PS> for Node<PS>
1082where
1083    Context<PS>: Parse<PS>,
1084{
1085    type Error = anyhow::Error;
1086
1087    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1088        let tail = bytes;
1089
1090        let num_bytes = tail.len();
1091        let (address, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
1092            type_name: "Node::address",
1093            type_size: std::mem::size_of::<le::U32>(),
1094            num_bytes,
1095        })?;
1096
1097        let num_bytes = tail.len();
1098        let (mask, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
1099            type_name: "Node::mask",
1100            type_size: std::mem::size_of::<le::U32>(),
1101            num_bytes,
1102        })?;
1103
1104        let (context, tail) = Context::parse(tail)
1105            .map_err(Into::<anyhow::Error>::into)
1106            .context("parsing context for node")?;
1107
1108        Ok((Self { address, mask, context }, tail))
1109    }
1110}
1111
1112impl<PS: ParseStrategy> Validate for Node<PS> {
1113    type Error = anyhow::Error;
1114
1115    /// TODO: Validate consistency between fields of [`Node`].
1116    fn validate(&self) -> Result<(), Self::Error> {
1117        Ok(())
1118    }
1119}
1120
1121pub(super) type FsUses<PS> = Vec<FsUse<PS>>;
1122
1123impl<PS: ParseStrategy> Validate for FsUses<PS> {
1124    type Error = anyhow::Error;
1125
1126    fn validate(&self) -> Result<(), Self::Error> {
1127        for fs_use in self {
1128            fs_use.validate()?;
1129        }
1130        Ok(())
1131    }
1132}
1133
1134#[derive(Debug, PartialEq)]
1135pub(super) struct FsUse<PS: ParseStrategy> {
1136    behavior_and_name: Array<PS, PS::Output<FsUseMetadata>, PS::Slice<u8>>,
1137    context: Context<PS>,
1138}
1139
1140impl<PS: ParseStrategy> FsUse<PS> {
1141    pub fn fs_type(&self) -> &[u8] {
1142        PS::deref_slice(&self.behavior_and_name.data)
1143    }
1144
1145    pub(super) fn behavior(&self) -> FsUseType {
1146        FsUseType::try_from(PS::deref(&self.behavior_and_name.metadata).behavior).unwrap()
1147    }
1148
1149    pub(super) fn context(&self) -> &Context<PS> {
1150        &self.context
1151    }
1152}
1153
1154impl<PS: ParseStrategy> Parse<PS> for FsUse<PS>
1155where
1156    Array<PS, PS::Output<FsUseMetadata>, PS::Slice<u8>>: Parse<PS>,
1157    Context<PS>: Parse<PS>,
1158{
1159    type Error = anyhow::Error;
1160
1161    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1162        let tail = bytes;
1163
1164        let (behavior_and_name, tail) =
1165            Array::<PS, PS::Output<FsUseMetadata>, PS::Slice<u8>>::parse(tail)
1166                .map_err(Into::<anyhow::Error>::into)
1167                .context("parsing fs use metadata")?;
1168
1169        let (context, tail) = Context::parse(tail)
1170            .map_err(Into::<anyhow::Error>::into)
1171            .context("parsing context for fs use")?;
1172
1173        Ok((Self { behavior_and_name, context }, tail))
1174    }
1175}
1176
1177impl<PS: ParseStrategy> Validate for FsUse<PS> {
1178    type Error = anyhow::Error;
1179
1180    fn validate(&self) -> Result<(), Self::Error> {
1181        FsUseType::try_from(PS::deref(&self.behavior_and_name.metadata).behavior)?;
1182
1183        Ok(())
1184    }
1185}
1186
1187#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1188#[repr(C, packed)]
1189pub(super) struct FsUseMetadata {
1190    /// The type of `fs_use` statement.
1191    behavior: le::U32,
1192    /// The length of the name in the name_and_behavior field of FsUse.
1193    name_length: le::U32,
1194}
1195
1196impl Counted for FsUseMetadata {
1197    fn count(&self) -> u32 {
1198        self.name_length.get()
1199    }
1200}
1201
1202/// Discriminates among the different kinds of "fs_use_*" labeling statements in the policy; see
1203/// https://selinuxproject.org/page/FileStatements.
1204#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
1205pub enum FsUseType {
1206    Xattr = 1,
1207    Trans = 2,
1208    Task = 3,
1209}
1210
1211impl TryFrom<le::U32> for FsUseType {
1212    type Error = anyhow::Error;
1213
1214    fn try_from(value: le::U32) -> Result<Self, Self::Error> {
1215        match value.get() {
1216            1 => Ok(FsUseType::Xattr),
1217            2 => Ok(FsUseType::Trans),
1218            3 => Ok(FsUseType::Task),
1219            _ => Err(ValidateError::InvalidFsUseType { value: value.get() }.into()),
1220        }
1221    }
1222}
1223
1224pub(super) type IPv6Nodes<PS> = Vec<IPv6Node<PS>>;
1225
1226impl<PS: ParseStrategy> Validate for IPv6Nodes<PS> {
1227    type Error = anyhow::Error;
1228
1229    /// TODO: Validate consistency of sequence of [`IPv6Node`] objects.
1230    fn validate(&self) -> Result<(), Self::Error> {
1231        Ok(())
1232    }
1233}
1234
1235#[derive(Debug, PartialEq)]
1236pub(super) struct IPv6Node<PS: ParseStrategy> {
1237    address: PS::Output<[le::U32; 4]>,
1238    mask: PS::Output<[le::U32; 4]>,
1239    context: Context<PS>,
1240}
1241
1242impl<PS: ParseStrategy> Parse<PS> for IPv6Node<PS>
1243where
1244    Context<PS>: Parse<PS>,
1245{
1246    type Error = anyhow::Error;
1247
1248    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1249        let tail = bytes;
1250
1251        let num_bytes = tail.len();
1252        let (address, tail) = PS::parse::<[le::U32; 4]>(tail).ok_or(ParseError::MissingData {
1253            type_name: "IPv6Node::address",
1254            type_size: std::mem::size_of::<le::U32>(),
1255            num_bytes,
1256        })?;
1257
1258        let num_bytes = tail.len();
1259        let (mask, tail) = PS::parse::<[le::U32; 4]>(tail).ok_or(ParseError::MissingData {
1260            type_name: "IPv6Node::mask",
1261            type_size: std::mem::size_of::<le::U32>(),
1262            num_bytes,
1263        })?;
1264
1265        let (context, tail) = Context::parse(tail)
1266            .map_err(Into::<anyhow::Error>::into)
1267            .context("parsing context for ipv6 node")?;
1268
1269        Ok((Self { address, mask, context }, tail))
1270    }
1271}
1272
1273pub(super) type InfinitiBandPartitionKeys<PS> = Vec<InfinitiBandPartitionKey<PS>>;
1274
1275impl<PS: ParseStrategy> Validate for InfinitiBandPartitionKeys<PS> {
1276    type Error = anyhow::Error;
1277
1278    /// TODO: Validate consistency of sequence of [`InfinitiBandPartitionKey`] objects.
1279    fn validate(&self) -> Result<(), Self::Error> {
1280        Ok(())
1281    }
1282}
1283
1284#[derive(Debug, PartialEq)]
1285pub(super) struct InfinitiBandPartitionKey<PS: ParseStrategy> {
1286    low: PS::Output<le::U32>,
1287    high: PS::Output<le::U32>,
1288    context: Context<PS>,
1289}
1290
1291impl<PS: ParseStrategy> Parse<PS> for InfinitiBandPartitionKey<PS>
1292where
1293    Context<PS>: Parse<PS>,
1294{
1295    type Error = anyhow::Error;
1296
1297    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1298        let tail = bytes;
1299
1300        let num_bytes = tail.len();
1301        let (low, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
1302            type_name: "InfinitiBandPartitionKey::low",
1303            type_size: std::mem::size_of::<le::U32>(),
1304            num_bytes,
1305        })?;
1306
1307        let num_bytes = tail.len();
1308        let (high, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
1309            type_name: "InfinitiBandPartitionKey::high",
1310            type_size: std::mem::size_of::<le::U32>(),
1311            num_bytes,
1312        })?;
1313
1314        let (context, tail) = Context::parse(tail)
1315            .map_err(Into::<anyhow::Error>::into)
1316            .context("parsing context for infiniti band partition key")?;
1317
1318        Ok((Self { low, high, context }, tail))
1319    }
1320}
1321
1322impl<PS: ParseStrategy> Validate for InfinitiBandPartitionKey<PS> {
1323    type Error = anyhow::Error;
1324
1325    /// TODO: Validate consistency between fields of [`InfinitiBandPartitionKey`].
1326    fn validate(&self) -> Result<(), Self::Error> {
1327        Ok(())
1328    }
1329}
1330
1331pub(super) type InfinitiBandEndPorts<PS> = Vec<InfinitiBandEndPort<PS>>;
1332
1333impl<PS: ParseStrategy> Validate for InfinitiBandEndPorts<PS> {
1334    type Error = anyhow::Error;
1335
1336    /// TODO: Validate sequence of [`InfinitiBandEndPort`] objects.
1337    fn validate(&self) -> Result<(), Self::Error> {
1338        Ok(())
1339    }
1340}
1341
1342#[derive(Debug, PartialEq)]
1343pub(super) struct InfinitiBandEndPort<PS: ParseStrategy> {
1344    port_and_name: Array<PS, PS::Output<InfinitiBandEndPortMetadata>, PS::Slice<u8>>,
1345    context: Context<PS>,
1346}
1347
1348impl<PS: ParseStrategy> Parse<PS> for InfinitiBandEndPort<PS>
1349where
1350    Array<PS, PS::Output<InfinitiBandEndPortMetadata>, PS::Slice<u8>>: Parse<PS>,
1351    Context<PS>: Parse<PS>,
1352{
1353    type Error = anyhow::Error;
1354
1355    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1356        let tail = bytes;
1357
1358        let (port_and_name, tail) =
1359            Array::<PS, PS::Output<InfinitiBandEndPortMetadata>, PS::Slice<u8>>::parse(tail)
1360                .map_err(Into::<anyhow::Error>::into)
1361                .context("parsing infiniti band end port metadata")?;
1362
1363        let (context, tail) = Context::parse(tail)
1364            .map_err(Into::<anyhow::Error>::into)
1365            .context("parsing context for infiniti band end port")?;
1366
1367        Ok((Self { port_and_name, context }, tail))
1368    }
1369}
1370
1371#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1372#[repr(C, packed)]
1373pub(super) struct InfinitiBandEndPortMetadata {
1374    length: le::U32,
1375    port: le::U32,
1376}
1377
1378impl Counted for InfinitiBandEndPortMetadata {
1379    fn count(&self) -> u32 {
1380        self.length.get()
1381    }
1382}
1383
1384pub(super) type GenericFsContexts<PS> = Vec<GenericFsContext<PS>>;
1385
1386impl<PS: ParseStrategy> Validate for GenericFsContexts<PS> {
1387    type Error = anyhow::Error;
1388
1389    /// TODO: Validate sequence of  [`GenericFsContext`] objects.
1390    fn validate(&self) -> Result<(), Self::Error> {
1391        Ok(())
1392    }
1393}
1394
1395/// Information parsed parsed from `genfscon [fs_type] [partial_path] [fs_context]` statements
1396/// about a specific filesystem type.
1397#[derive(Debug, PartialEq)]
1398pub(super) struct GenericFsContext<PS: ParseStrategy> {
1399    /// The filesystem type.
1400    fs_type: SimpleArray<PS, PS::Slice<u8>>,
1401    /// The set of contexts defined for this filesystem.
1402    contexts: SimpleArray<PS, FsContexts<PS>>,
1403}
1404
1405impl<PS: ParseStrategy> GenericFsContext<PS> {
1406    pub(super) fn fs_type(&self) -> &[u8] {
1407        PS::deref_slice(&self.fs_type.data)
1408    }
1409
1410    pub(super) fn contexts(&self) -> &FsContexts<PS> {
1411        &self.contexts.data
1412    }
1413}
1414
1415impl<PS: ParseStrategy> Parse<PS> for GenericFsContext<PS>
1416where
1417    SimpleArray<PS, PS::Slice<u8>>: Parse<PS>,
1418    SimpleArray<PS, FsContexts<PS>>: Parse<PS>,
1419{
1420    type Error = anyhow::Error;
1421
1422    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1423        let tail = bytes;
1424
1425        let (fs_type, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail)
1426            .map_err(Into::<anyhow::Error>::into)
1427            .context("parsing generic filesystem context name")?;
1428
1429        let (contexts, tail) = SimpleArray::<PS, FsContexts<PS>>::parse(tail)
1430            .map_err(Into::<anyhow::Error>::into)
1431            .context("parsing generic filesystem contexts")?;
1432
1433        Ok((Self { fs_type, contexts }, tail))
1434    }
1435}
1436
1437pub(super) type FsContexts<PS> = Vec<FsContext<PS>>;
1438
1439#[derive(Debug, PartialEq)]
1440pub(super) struct FsContext<PS: ParseStrategy> {
1441    /// The partial path, relative to the root of the filesystem. The partial path can only be set for
1442    /// virtual filesystems, like `proc/`. Otherwise, this must be `/`
1443    partial_path: SimpleArray<PS, PS::Slice<u8>>,
1444    /// Optional. When provided, the context will only be applied to files of this type. Allowed files
1445    /// types are: blk_file, chr_file, dir, fifo_file, lnk_file, sock_file, file. When set to 0, the
1446    /// context applies to all file types.
1447    class: PS::Output<le::U32>,
1448    /// The security context allocated to the filesystem.
1449    context: Context<PS>,
1450}
1451
1452impl<PS: ParseStrategy> FsContext<PS> {
1453    pub(super) fn partial_path(&self) -> &[u8] {
1454        PS::deref_slice(&self.partial_path.data)
1455    }
1456
1457    pub(super) fn context(&self) -> &Context<PS> {
1458        &self.context
1459    }
1460
1461    pub(super) fn class(&self) -> Option<ClassId> {
1462        NonZeroU32::new((*PS::deref(&self.class)).into()).map(ClassId)
1463    }
1464}
1465
1466impl<PS: ParseStrategy> Parse<PS> for FsContext<PS>
1467where
1468    SimpleArray<PS, PS::Slice<u8>>: Parse<PS>,
1469    Context<PS>: Parse<PS>,
1470{
1471    type Error = anyhow::Error;
1472
1473    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1474        let tail = bytes;
1475
1476        let (partial_path, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail)
1477            .map_err(Into::<anyhow::Error>::into)
1478            .context("parsing filesystem context partial path")?;
1479
1480        let num_bytes = tail.len();
1481        let (class, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData {
1482            type_name: "FsContext::class",
1483            type_size: std::mem::size_of::<le::U32>(),
1484            num_bytes,
1485        })?;
1486
1487        let (context, tail) = Context::parse(tail)
1488            .map_err(Into::<anyhow::Error>::into)
1489            .context("parsing context for filesystem context")?;
1490
1491        Ok((Self { partial_path, class, context }, tail))
1492    }
1493}
1494
1495pub(super) type RangeTransitions<PS> = Vec<RangeTransition<PS>>;
1496
1497impl<PS: ParseStrategy> Validate for RangeTransitions<PS> {
1498    type Error = anyhow::Error;
1499
1500    /// TODO: Validate sequence of [`RangeTransition`] objects.
1501    fn validate(&self) -> Result<(), Self::Error> {
1502        for range_transition in self {
1503            if PS::deref(&range_transition.metadata).target_class.get() == 0 {
1504                return Err(ValidateError::NonOptionalIdIsZero.into());
1505            }
1506        }
1507        Ok(())
1508    }
1509}
1510
1511#[derive(Debug, PartialEq)]
1512pub(super) struct RangeTransition<PS: ParseStrategy> {
1513    metadata: PS::Output<RangeTransitionMetadata>,
1514    mls_range: MlsRange<PS>,
1515}
1516
1517impl<PS: ParseStrategy> RangeTransition<PS> {
1518    pub fn source_type(&self) -> TypeId {
1519        TypeId(NonZeroU32::new(PS::deref(&self.metadata).source_type.get()).unwrap())
1520    }
1521
1522    pub fn target_type(&self) -> TypeId {
1523        TypeId(NonZeroU32::new(PS::deref(&self.metadata).target_type.get()).unwrap())
1524    }
1525
1526    pub fn target_class(&self) -> ClassId {
1527        ClassId(NonZeroU32::new(PS::deref(&self.metadata).target_class.get()).unwrap())
1528    }
1529
1530    pub fn mls_range(&self) -> &MlsRange<PS> {
1531        &self.mls_range
1532    }
1533}
1534
1535impl<PS: ParseStrategy> Parse<PS> for RangeTransition<PS>
1536where
1537    MlsRange<PS>: Parse<PS>,
1538{
1539    type Error = anyhow::Error;
1540
1541    fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
1542        let tail = bytes;
1543
1544        let (metadata, tail) = PS::parse::<RangeTransitionMetadata>(tail)
1545            .context("parsing range transition metadata")?;
1546
1547        let (mls_range, tail) = MlsRange::parse(tail)
1548            .map_err(Into::<anyhow::Error>::into)
1549            .context("parsing mls range for range transition")?;
1550
1551        Ok((Self { metadata, mls_range }, tail))
1552    }
1553}
1554
1555#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
1556#[repr(C, packed)]
1557pub(super) struct RangeTransitionMetadata {
1558    source_type: le::U32,
1559    target_type: le::U32,
1560    target_class: le::U32,
1561}
1562
1563#[cfg(test)]
1564mod tests {
1565    use super::super::{find_class_by_name, parse_policy_by_reference};
1566    use super::*;
1567
1568    #[test]
1569    fn parse_allowxperm_one_ioctl() {
1570        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1571        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1572        let parsed_policy = &policy.0;
1573        Validate::validate(parsed_policy).expect("validate policy");
1574
1575        let class_id = find_class_by_name(parsed_policy.classes(), "class_one_ioctl")
1576            .expect("look up class_one_ioctl")
1577            .id();
1578
1579        let rules: Vec<_> = parsed_policy
1580            .access_vector_rules()
1581            .into_iter()
1582            .filter(|rule| rule.target_class() == class_id)
1583            .collect();
1584
1585        assert_eq!(rules.len(), 1);
1586        assert!(rules[0].is_allowxperm());
1587        if let Some(xperms) = rules[0].extended_permissions() {
1588            assert_eq!(xperms.count(), 1);
1589            assert!(xperms.contains(0xabcd));
1590        } else {
1591            panic!("unexpected permission data type")
1592        }
1593    }
1594
1595    // Extended permissions that are declared in the same rule, and have the same
1596    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1597    #[test]
1598    fn parse_allowxperm_two_ioctls_same_range() {
1599        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1600        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1601        let parsed_policy = &policy.0;
1602        Validate::validate(parsed_policy).expect("validate policy");
1603
1604        let class_id = find_class_by_name(parsed_policy.classes(), "class_two_ioctls_same_range")
1605            .expect("look up class_two_ioctls_same_range")
1606            .id();
1607
1608        let rules: Vec<_> = parsed_policy
1609            .access_vector_rules()
1610            .into_iter()
1611            .filter(|rule| rule.target_class() == class_id)
1612            .collect();
1613
1614        assert_eq!(rules.len(), 1);
1615        assert!(rules[0].is_allowxperm());
1616        if let Some(xperms) = rules[0].extended_permissions() {
1617            assert_eq!(xperms.count(), 2);
1618            assert!(xperms.contains(0x1234));
1619            assert!(xperms.contains(0x1256));
1620        } else {
1621            panic!("unexpected permission data type")
1622        }
1623    }
1624
1625    // Extended permissions that are declared in the same rule, and have different
1626    // high bytes, are stored in different `AccessVectorRule`s in the compiled policy.
1627    #[test]
1628    fn parse_allowxperm_two_ioctls_different_range() {
1629        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1630        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1631        let parsed_policy = &policy.0;
1632        Validate::validate(parsed_policy).expect("validate policy");
1633
1634        let class_id = find_class_by_name(parsed_policy.classes(), "class_two_ioctls_diff_range")
1635            .expect("look up class_two_ioctls_diff_range")
1636            .id();
1637
1638        let rules: Vec<_> = parsed_policy
1639            .access_vector_rules()
1640            .into_iter()
1641            .filter(|rule| rule.target_class() == class_id)
1642            .collect();
1643
1644        assert_eq!(rules.len(), 2);
1645        assert!(rules[0].is_allowxperm());
1646        if let Some(xperms) = rules[0].extended_permissions() {
1647            assert_eq!(xperms.count(), 1);
1648            assert!(xperms.contains(0x5678));
1649        } else {
1650            panic!("unexpected permission data type")
1651        }
1652        assert!(rules[1].is_allowxperm());
1653        if let Some(xperms) = rules[1].extended_permissions() {
1654            assert_eq!(xperms.count(), 1);
1655            assert!(xperms.contains(0x1234));
1656        } else {
1657            panic!("unexpected permission data type")
1658        }
1659    }
1660
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_reference(policy_bytes.as_slice()).expect("parse policy");
1665        let parsed_policy = &policy.0;
1666        Validate::validate(parsed_policy).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()
1674            .into_iter()
1675            .filter(|rule| rule.target_class() == class_id)
1676            .collect();
1677
1678        assert_eq!(rules.len(), 1);
1679        assert!(rules[0].is_allowxperm());
1680        if let Some(xperms) = rules[0].extended_permissions() {
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    #[test]
1690    fn parse_allowxperm_all_ioctls() {
1691        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1692        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1693        let parsed_policy = &policy.0;
1694        Validate::validate(parsed_policy).expect("validate policy");
1695
1696        let class_id = find_class_by_name(parsed_policy.classes(), "class_all_ioctls")
1697            .expect("look up class_all_ioctls")
1698            .id();
1699
1700        let rules: Vec<_> = parsed_policy
1701            .access_vector_rules()
1702            .into_iter()
1703            .filter(|rule| rule.target_class() == class_id)
1704            .collect();
1705
1706        assert_eq!(rules.len(), 1);
1707        assert!(rules[0].is_allowxperm());
1708        if let Some(xperms) = rules[0].extended_permissions() {
1709            assert_eq!(xperms.count(), 0x10000);
1710        } else {
1711            panic!("unexpected permission data type")
1712        }
1713    }
1714
1715    // Distinct xperm rules with the same source, target, class, and permission are
1716    // represented by distinct `AccessVectorRule`s in the compiled policy, even if
1717    // they have overlapping xperm ranges.
1718    #[test]
1719    fn parse_allowxperm_overlapping_ranges() {
1720        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1721        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1722        let parsed_policy = &policy.0;
1723        Validate::validate(parsed_policy).expect("validate policy");
1724
1725        let class_id = find_class_by_name(parsed_policy.classes(), "class_overlapping_ranges")
1726            .expect("look up class_overlapping_ranges")
1727            .id();
1728
1729        let rules: Vec<_> = parsed_policy
1730            .access_vector_rules()
1731            .into_iter()
1732            .filter(|rule| rule.target_class() == class_id)
1733            .collect();
1734
1735        assert_eq!(rules.len(), 2);
1736        assert!(rules[0].is_allowxperm());
1737        if let Some(xperms) = rules[0].extended_permissions() {
1738            assert_eq!(xperms.count(), 0x100);
1739            // Any ioctl in the range 0x10?? should be in the set.
1740            assert!(xperms.contains(0x1000));
1741            assert!(xperms.contains(0x10ab));
1742        } else {
1743            panic!("unexpected permission data type")
1744        }
1745        assert!(rules[1].is_allowxperm());
1746        if let Some(xperms) = rules[1].extended_permissions() {
1747            assert_eq!(xperms.count(), 2);
1748            assert!(xperms.contains(0x1000));
1749            assert!(xperms.contains(0x1001));
1750        } else {
1751            panic!("unexpected permission data type")
1752        }
1753    }
1754
1755    // The representation of extended permissions for `auditallowxperm` rules is
1756    // the same as for `allowxperm` rules.
1757    #[test]
1758    fn parse_auditallowxperm() {
1759        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1760        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1761        let parsed_policy = &policy.0;
1762        Validate::validate(parsed_policy).expect("validate policy");
1763
1764        let class_id = find_class_by_name(parsed_policy.classes(), "class_auditallowxperm")
1765            .expect("look up class_auditallowxperm")
1766            .id();
1767
1768        let rules: Vec<_> = parsed_policy
1769            .access_vector_rules()
1770            .into_iter()
1771            .filter(|rule| rule.target_class() == class_id)
1772            .collect();
1773
1774        assert_eq!(rules.len(), 1);
1775        assert!(rules[0].is_auditallowxperm());
1776        if let Some(xperms) = rules[0].extended_permissions() {
1777            assert_eq!(xperms.count(), 1);
1778            assert!(xperms.contains(0x1000));
1779        } else {
1780            panic!("unexpected permission data type")
1781        }
1782    }
1783
1784    // The representation of extended permissions for `dontauditxperm` rules is
1785    // the same as for `allowxperm` rules. In particular, the `AccessVectorRule`
1786    // contains the same set of extended permissions that appears in the text
1787    // policy. (This differs from the representation of the access vector in
1788    // `AccessVectorRule`s for `dontaudit` rules, where the `AccessVectorRule`
1789    // contains the complement of the access vector that appears in the text
1790    // policy.)
1791    #[test]
1792    fn parse_dontauditxperm() {
1793        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1794        let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1795        let parsed_policy = &policy.0;
1796        Validate::validate(parsed_policy).expect("validate policy");
1797
1798        let class_id = find_class_by_name(parsed_policy.classes(), "class_dontauditxperm")
1799            .expect("look up class_dontauditxperm")
1800            .id();
1801
1802        let rules: Vec<_> = parsed_policy
1803            .access_vector_rules()
1804            .into_iter()
1805            .filter(|rule| rule.target_class() == class_id)
1806            .collect();
1807
1808        assert_eq!(rules.len(), 1);
1809        assert!(rules[0].is_dontauditxperm());
1810        if let Some(xperms) = rules[0].extended_permissions() {
1811            assert_eq!(xperms.count(), 1);
1812            assert!(xperms.contains(0x1000));
1813        } else {
1814            panic!("unexpected permission data type")
1815        }
1816    }
1817}