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