| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Special cases of `Array<Bytes, Metadata, Data>` and instances of `Metadata` and `Data` that |
| //! appear in binary SELinux policies. |
| |
| use super::{ |
| array_type, array_type_validate_deref_both, |
| error::{ParseError, ValidateError}, |
| extensible_bitmap::ExtensibleBitmap, |
| parser::ParseStrategy, |
| symbols::{MlsLevel, MlsRange}, |
| Array, Counted, Parse, RoleId, TypeId, UserId, Validate, ValidateArray, |
| }; |
| |
| use anyhow::Context as _; |
| use selinux_common as sc; |
| use std::{fmt::Debug, num::NonZeroU32}; |
| use zerocopy::{little_endian as le, FromBytes, FromZeroes, NoCell, Unaligned}; |
| |
| pub(crate) const EXTENDED_PERMISSIONS_IS_SPECIFIED_DRIVER_PERMISSIONS_MASK: u16 = 0x0700; |
| pub(crate) const MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY: u32 = 31; |
| |
| /// Mask for [`AccessVectorMetadata`] `access_vector_type` that indicates whether the access vector |
| /// comes from an `allow [source] [target]:[class] { [permissions] };` policy statement. |
| pub(crate) const ACCESS_VECTOR_TYPE_ALLOW_MASK: u16 = 1; |
| |
| /// Mask for [`AccessVectorMetadata`] `access_vector_type` that indicates whether the access vector |
| /// comes from an `allow [source] [target]:[class] { [permissions] };` policy statement. |
| pub(crate) const ACCESS_VECTOR_TYPE_TYPE_TRANSITION_MASK: u16 = 16; |
| |
| #[allow(type_alias_bounds)] |
| pub(crate) type SimpleArray<PS: ParseStrategy, T> = Array<PS, PS::Output<le::U32>, T>; |
| |
| impl<PS: ParseStrategy, T: Validate> Validate for SimpleArray<PS, T> { |
| type Error = <T as Validate>::Error; |
| |
| /// Defers to `self.data` for validation. `self.data` has access to all information, including |
| /// size stored in `self.metadata`. |
| fn validate(&self) -> Result<(), Self::Error> { |
| self.data.validate() |
| } |
| } |
| |
| impl Counted for le::U32 { |
| fn count(&self) -> u32 { |
| self.get() |
| } |
| } |
| |
| pub(crate) type ConditionalNodes<PS> = Vec<ConditionalNode<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for ConditionalNodes<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate internal consistency between consecutive [`ConditionalNode`] instances. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| array_type!( |
| ConditionalNodeItems, |
| PS, |
| PS::Output<ConditionalNodeMetadata>, |
| PS::Slice<ConditionalNodeDatum> |
| ); |
| |
| array_type_validate_deref_both!(ConditionalNodeItems); |
| |
| impl<PS: ParseStrategy> ValidateArray<ConditionalNodeMetadata, ConditionalNodeDatum> |
| for ConditionalNodeItems<PS> |
| { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate internal consistency between [`ConditionalNodeMetadata`] consecutive |
| /// [`ConditionalNodeDatum`]. |
| fn validate_array<'a>( |
| _metadata: &'a ConditionalNodeMetadata, |
| _data: &'a [ConditionalNodeDatum], |
| ) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct ConditionalNode<PS: ParseStrategy> { |
| items: ConditionalNodeItems<PS>, |
| true_list: SimpleArray<PS, AccessVectors<PS>>, |
| false_list: SimpleArray<PS, AccessVectors<PS>>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for ConditionalNode<PS> |
| where |
| ConditionalNodeItems<PS>: Parse<PS>, |
| SimpleArray<PS, AccessVectors<PS>>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (items, tail) = ConditionalNodeItems::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing conditional node items")?; |
| |
| let (true_list, tail) = SimpleArray::<PS, AccessVectors<PS>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing conditional node true list")?; |
| |
| let (false_list, tail) = SimpleArray::<PS, AccessVectors<PS>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing conditional node false list")?; |
| |
| Ok((Self { items, true_list, false_list }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct ConditionalNodeMetadata { |
| state: le::U32, |
| count: le::U32, |
| } |
| |
| impl Counted for ConditionalNodeMetadata { |
| fn count(&self) -> u32 { |
| self.count.get() |
| } |
| } |
| |
| impl Validate for ConditionalNodeMetadata { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate [`ConditionalNodeMetadata`] internals. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct ConditionalNodeDatum { |
| node_type: le::U32, |
| boolean: le::U32, |
| } |
| |
| impl Validate for [ConditionalNodeDatum] { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`ConditionalNodeDatum`]. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| pub(crate) type AccessVectors<PS> = Vec<AccessVector<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for AccessVectors<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate internal consistency between consecutive [`AccessVector`] instances. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct AccessVector<PS: ParseStrategy> { |
| metadata: PS::Output<AccessVectorMetadata>, |
| extended_permissions: ExtendedPermissions<PS>, |
| } |
| |
| impl<PS: ParseStrategy> AccessVector<PS> { |
| /// Returns whether this access vector comes from an |
| /// `allow [source] [target]:[class] { [permissions] };` policy statement. |
| pub fn is_allow(&self) -> bool { |
| PS::deref(&self.metadata).is_allow() |
| } |
| |
| /// Returns whether this access vector compes from a |
| /// `type_transtion [source] [target]:[class] [new_type];` policy statement. |
| pub fn is_type_transition(&self) -> bool { |
| PS::deref(&self.metadata).is_type_transition() |
| } |
| |
| /// Returns the source type id in this access vector. This id corresponds to the |
| /// [`super::symbols::Type`] `id()` of some type or attribute in the same policy. |
| pub fn source_type(&self) -> TypeId { |
| TypeId(NonZeroU32::new(PS::deref(&self.metadata).source_type.into()).unwrap()) |
| } |
| |
| /// Returns the target type id in this access vector. This id corresponds to the |
| /// [`super::symbols::Type`] `id()` of some type or attribute in the same policy. |
| pub fn target_type(&self) -> TypeId { |
| TypeId(NonZeroU32::new(PS::deref(&self.metadata).target_type.into()).unwrap()) |
| } |
| |
| /// Returns the target class id in this access vector. This id corresponds to the |
| /// [`super::symbols::Class`] `id()` of some class in the same policy. |
| /// Although the index is returned as a 32-bit value, the field itself is 16-bit |
| pub fn target_class(&self) -> le::U32 { |
| PS::deref(&self.metadata).class.into() |
| } |
| |
| /// A bit mask that corresponds to the permissions in this access vector. Permission bits are |
| /// specified using a [`super::symbols::Permission`] `id()` as follows: |
| /// `1 << (Permission::id() - 1)`. |
| pub fn permission_mask(&self) -> Option<le::U32> { |
| match &self.extended_permissions { |
| ExtendedPermissions::PermissionMask(mask) => Some(*PS::deref(mask)), |
| _ => None, |
| } |
| } |
| |
| /// A numeric type id that corresponds to a the `[new_type]` in a |
| /// `type_transition [source] [target]:[class] [new_type];` policy statement. |
| pub fn new_type(&self) -> Option<TypeId> { |
| match &self.extended_permissions { |
| ExtendedPermissions::NewType(new_type) => { |
| Some(TypeId(NonZeroU32::new(PS::deref(new_type).get().into()).unwrap())) |
| } |
| _ => None, |
| } |
| } |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for AccessVector<PS> { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let num_bytes = tail.len(); |
| let (metadata, tail) = |
| PS::parse::<AccessVectorMetadata>(tail).ok_or(ParseError::MissingData { |
| type_name: std::any::type_name::<AccessVectorMetadata>(), |
| type_size: std::mem::size_of::<AccessVectorMetadata>(), |
| num_bytes, |
| })?; |
| |
| let access_vector_type = PS::deref(&metadata).access_vector_type(); |
| let (extended_permissions, tail) = if (access_vector_type |
| & EXTENDED_PERMISSIONS_IS_SPECIFIED_DRIVER_PERMISSIONS_MASK) |
| != 0 |
| { |
| let num_bytes = tail.len(); |
| let (specified_driver_permissions, tail) = |
| PS::parse::<SpecifiedDriverPermissions>(tail).ok_or(ParseError::MissingData { |
| type_name: std::any::type_name::<SpecifiedDriverPermissions>(), |
| type_size: std::mem::size_of::<SpecifiedDriverPermissions>(), |
| num_bytes, |
| })?; |
| (ExtendedPermissions::SpecifiedDriverPermissions(specified_driver_permissions), tail) |
| } else { |
| let num_bytes = tail.len(); |
| let (extended_permissions, tail) = |
| PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "ExtendedPermissions::PermissionMask", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| if (access_vector_type & ACCESS_VECTOR_TYPE_TYPE_TRANSITION_MASK) != 0 { |
| (ExtendedPermissions::NewType(extended_permissions), tail) |
| } else { |
| (ExtendedPermissions::PermissionMask(extended_permissions), tail) |
| } |
| }; |
| |
| Ok((Self { metadata, extended_permissions }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct AccessVectorMetadata { |
| source_type: le::U16, |
| target_type: le::U16, |
| class: le::U16, |
| access_vector_type: le::U16, |
| } |
| |
| impl AccessVectorMetadata { |
| /// Returns the access vector type field that indicates the type of policy statement this |
| /// access vector comes from; for example `allow ...;`, `auditallow ...;`. |
| pub fn access_vector_type(&self) -> u16 { |
| self.access_vector_type.get() |
| } |
| |
| /// Returns whether this access vector comes from an |
| /// `allow [source] [target]:[class] { [permissions] };` policy statement. |
| pub fn is_allow(&self) -> bool { |
| (self.access_vector_type() & ACCESS_VECTOR_TYPE_ALLOW_MASK) != 0 |
| } |
| |
| /// Returns whether this access vector compes from a |
| /// `type_transtion [source] [target]:[class] [new_type];` policy statement. |
| pub fn is_type_transition(&self) -> bool { |
| (self.access_vector_type() & ACCESS_VECTOR_TYPE_TYPE_TRANSITION_MASK) != 0 |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) enum ExtendedPermissions<PS: ParseStrategy> { |
| SpecifiedDriverPermissions(PS::Output<SpecifiedDriverPermissions>), |
| PermissionMask(PS::Output<le::U32>), |
| NewType(PS::Output<le::U32>), |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct SpecifiedDriverPermissions { |
| specified: u8, |
| driver: u8, |
| permissions: [le::U32; 8], |
| } |
| |
| array_type!(RoleTransitions, PS, PS::Output<le::U32>, PS::Slice<RoleTransition>); |
| |
| array_type_validate_deref_both!(RoleTransitions); |
| |
| impl<PS: ParseStrategy> ValidateArray<le::U32, RoleTransition> for RoleTransitions<PS> { |
| type Error = anyhow::Error; |
| |
| /// [`RoleTransitions`] have no additional metadata (beyond length encoding). |
| fn validate_array<'a>( |
| _metadata: &'a le::U32, |
| _data: &'a [RoleTransition], |
| ) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct RoleTransition { |
| role: le::U32, |
| role_type: le::U32, |
| new_role: le::U32, |
| tclass: le::U32, |
| } |
| |
| impl RoleTransition { |
| pub(crate) fn current_role(&self) -> RoleId { |
| RoleId(NonZeroU32::new(self.role.get()).unwrap()) |
| } |
| |
| pub(crate) fn type_(&self) -> TypeId { |
| TypeId(NonZeroU32::new(self.role_type.get()).unwrap()) |
| } |
| |
| pub(crate) fn class(&self) -> le::U32 { |
| self.tclass |
| } |
| |
| pub(crate) fn new_role(&self) -> RoleId { |
| RoleId(NonZeroU32::new(self.new_role.get()).unwrap()) |
| } |
| } |
| |
| impl Validate for [RoleTransition] { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`RoleTransition`]. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| array_type!(RoleAllows, PS, PS::Output<le::U32>, PS::Slice<RoleAllow>); |
| |
| array_type_validate_deref_both!(RoleAllows); |
| |
| impl<PS: ParseStrategy> ValidateArray<le::U32, RoleAllow> for RoleAllows<PS> { |
| type Error = anyhow::Error; |
| |
| /// [`RoleAllows`] have no additional metadata (beyond length encoding). |
| fn validate_array<'a>( |
| _metadata: &'a le::U32, |
| _data: &'a [RoleAllow], |
| ) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct RoleAllow { |
| role: le::U32, |
| new_role: le::U32, |
| } |
| |
| impl RoleAllow { |
| #[allow(dead_code)] |
| // TODO(http://b/334968228): fn to be used again when checking role allow rules separately from |
| // SID calculation. |
| pub(crate) fn source_role(&self) -> RoleId { |
| RoleId(NonZeroU32::new(self.role.get()).unwrap()) |
| } |
| |
| #[allow(dead_code)] |
| // TODO(http://b/334968228): fn to be used again when checking role allow rules separately from |
| // SID calculation. |
| pub(crate) fn new_role(&self) -> RoleId { |
| RoleId(NonZeroU32::new(self.new_role.get()).unwrap()) |
| } |
| } |
| |
| impl Validate for [RoleAllow] { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`RoleAllow`]. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) enum FilenameTransitionList<PS: ParseStrategy> { |
| PolicyVersionGeq33(SimpleArray<PS, FilenameTransitions<PS>>), |
| PolicyVersionLeq32(SimpleArray<PS, DeprecatedFilenameTransitions<PS>>), |
| } |
| |
| impl<PS: ParseStrategy> Validate for FilenameTransitionList<PS> { |
| type Error = anyhow::Error; |
| |
| fn validate(&self) -> Result<(), Self::Error> { |
| match self { |
| Self::PolicyVersionLeq32(list) => list.validate().map_err(Into::<anyhow::Error>::into), |
| Self::PolicyVersionGeq33(list) => list.validate().map_err(Into::<anyhow::Error>::into), |
| } |
| } |
| } |
| |
| pub(crate) type FilenameTransitions<PS> = Vec<FilenameTransition<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for FilenameTransitions<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`FilenameTransition`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct FilenameTransition<PS: ParseStrategy> { |
| filename: SimpleArray<PS, PS::Slice<u8>>, |
| transition_type: PS::Output<le::U32>, |
| transition_class: PS::Output<le::U32>, |
| items: SimpleArray<PS, FilenameTransitionItems<PS>>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for FilenameTransition<PS> |
| where |
| SimpleArray<PS, PS::Slice<u8>>: Parse<PS>, |
| SimpleArray<PS, FilenameTransitionItems<PS>>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (filename, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing filename for filename transition")?; |
| |
| let num_bytes = tail.len(); |
| let (transition_type, tail) = |
| PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "FilenameTransition::transition_type", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let num_bytes = tail.len(); |
| let (transition_class, tail) = |
| PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "FilenameTransition::transition_class", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let (items, tail) = SimpleArray::<PS, FilenameTransitionItems<PS>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing items for filename transition")?; |
| |
| Ok((Self { filename, transition_type, transition_class, items }, tail)) |
| } |
| } |
| |
| pub(crate) type FilenameTransitionItems<PS> = Vec<FilenameTransitionItem<PS>>; |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct FilenameTransitionItem<PS: ParseStrategy> { |
| stypes: ExtensibleBitmap<PS>, |
| out_type: PS::Output<le::U32>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for FilenameTransitionItem<PS> |
| where |
| ExtensibleBitmap<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (stypes, tail) = ExtensibleBitmap::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing stypes extensible bitmap for file transition")?; |
| |
| let num_bytes = tail.len(); |
| let (out_type, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "FilenameTransitionItem::out_type", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| Ok((Self { stypes, out_type }, tail)) |
| } |
| } |
| |
| pub(crate) type DeprecatedFilenameTransitions<PS> = Vec<DeprecatedFilenameTransition<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for DeprecatedFilenameTransitions<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`DeprecatedFilenameTransition`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct DeprecatedFilenameTransition<PS: ParseStrategy> { |
| filename: SimpleArray<PS, PS::Slice<u8>>, |
| metadata: PS::Output<DeprecatedFilenameTransitionMetadata>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for DeprecatedFilenameTransition<PS> |
| where |
| SimpleArray<PS, PS::Slice<u8>>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (filename, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing filename for deprecated filename transition")?; |
| |
| let num_bytes = tail.len(); |
| let (metadata, tail) = PS::parse::<DeprecatedFilenameTransitionMetadata>(tail).ok_or( |
| ParseError::MissingData { |
| type_name: "DeprecatedFilenameTransition::metadata", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| }, |
| )?; |
| |
| Ok((Self { filename, metadata }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct DeprecatedFilenameTransitionMetadata { |
| bit: le::U32, |
| transition_type: le::U32, |
| transition_class: le::U32, |
| old_type: le::U32, |
| } |
| |
| pub(crate) type InitialSids<PS> = Vec<InitialSid<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for InitialSids<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency of sequence of [`InitialSid`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| for initial_sid in sc::InitialSid::all_variants() { |
| self.iter() |
| .find(|initial| initial.id().get() == initial_sid as u32) |
| .ok_or(ValidateError::MissingInitialSid { initial_sid })?; |
| } |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct InitialSid<PS: ParseStrategy> { |
| id: PS::Output<le::U32>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> InitialSid<PS> { |
| pub(crate) fn id(&self) -> le::U32 { |
| *PS::deref(&self.id) |
| } |
| |
| pub(crate) fn context(&self) -> &Context<PS> { |
| &self.context |
| } |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for InitialSid<PS> |
| where |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let num_bytes = tail.len(); |
| let (id, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "InitialSid::sid", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for initial sid")?; |
| |
| Ok((Self { id, context }, tail)) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct Context<PS: ParseStrategy> { |
| metadata: PS::Output<ContextMetadata>, |
| mls_range: MlsRange<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Context<PS> { |
| pub(crate) fn user_id(&self) -> UserId { |
| UserId(NonZeroU32::new(PS::deref(&self.metadata).user.get()).unwrap()) |
| } |
| pub(crate) fn role_id(&self) -> RoleId { |
| RoleId(NonZeroU32::new(PS::deref(&self.metadata).role.get()).unwrap()) |
| } |
| pub(crate) fn type_id(&self) -> TypeId { |
| TypeId(NonZeroU32::new(PS::deref(&self.metadata).context_type.get()).unwrap()) |
| } |
| pub(crate) fn low_level(&self) -> &MlsLevel<PS> { |
| self.mls_range.low() |
| } |
| pub(crate) fn high_level(&self) -> &Option<MlsLevel<PS>> { |
| self.mls_range.high() |
| } |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for Context<PS> |
| where |
| MlsRange<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (metadata, tail) = |
| PS::parse::<ContextMetadata>(tail).context("parsing metadata for context")?; |
| |
| let (mls_range, tail) = MlsRange::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing mls range for context")?; |
| |
| Ok((Self { metadata, mls_range }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct ContextMetadata { |
| user: le::U32, |
| role: le::U32, |
| context_type: le::U32, |
| } |
| |
| pub(crate) type NamedContextPairs<PS> = Vec<NamedContextPair<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for NamedContextPairs<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency of sequence of [`NamedContextPairs`] objects. |
| /// |
| /// TODO: Is different validation required for `filesystems` and `network_interfaces`? If so, |
| /// create wrapper types with different [`Validate`] implementations. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct NamedContextPair<PS: ParseStrategy> { |
| name: SimpleArray<PS, PS::Slice<u8>>, |
| context1: Context<PS>, |
| context2: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for NamedContextPair<PS> |
| where |
| SimpleArray<PS, PS::Slice<u8>>: Parse<PS>, |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (name, tail) = SimpleArray::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing filesystem context name")?; |
| |
| let (context1, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing first context for filesystem context")?; |
| |
| let (context2, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing second context for filesystem context")?; |
| |
| Ok((Self { name, context1, context2 }, tail)) |
| } |
| } |
| |
| pub(crate) type Ports<PS> = Vec<Port<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for Ports<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency of sequence of [`Ports`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct Port<PS: ParseStrategy> { |
| metadata: PS::Output<PortMetadata>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for Port<PS> |
| where |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (metadata, tail) = |
| PS::parse::<PortMetadata>(tail).context("parsing metadata for context")?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for port")?; |
| |
| Ok((Self { metadata, context }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct PortMetadata { |
| protocol: le::U32, |
| low_port: le::U32, |
| high_port: le::U32, |
| } |
| |
| pub(crate) type Nodes<PS> = Vec<Node<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for Nodes<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency of sequence of [`Node`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct Node<PS: ParseStrategy> { |
| address: PS::Output<le::U32>, |
| mask: PS::Output<le::U32>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for Node<PS> |
| where |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let num_bytes = tail.len(); |
| let (address, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "Node::address", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let num_bytes = tail.len(); |
| let (mask, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "Node::mask", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for node")?; |
| |
| Ok((Self { address, mask, context }, tail)) |
| } |
| } |
| |
| impl<PS: ParseStrategy> Validate for Node<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency between fields of [`Node`]. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| pub(crate) type FsUses<PS> = Vec<FsUse<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for FsUses<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`FsUse`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct FsUse<PS: ParseStrategy> { |
| behavior_and_name: Array<PS, PS::Output<FsUseMetadata>, PS::Slice<u8>>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for FsUse<PS> |
| where |
| Array<PS, PS::Output<FsUseMetadata>, PS::Slice<u8>>: Parse<PS>, |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (behavior_and_name, tail) = |
| Array::<PS, PS::Output<FsUseMetadata>, PS::Slice<u8>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing fs use metadata")?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for fs use")?; |
| |
| Ok((Self { behavior_and_name, context }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct FsUseMetadata { |
| behavior: le::U32, |
| length: le::U32, |
| } |
| |
| impl Counted for FsUseMetadata { |
| fn count(&self) -> u32 { |
| self.length.get() |
| } |
| } |
| |
| pub(crate) type IPv6Nodes<PS> = Vec<IPv6Node<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for IPv6Nodes<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency of sequence of [`IPv6Node`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct IPv6Node<PS: ParseStrategy> { |
| address: PS::Output<[le::U32; 4]>, |
| mask: PS::Output<[le::U32; 4]>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for IPv6Node<PS> |
| where |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let num_bytes = tail.len(); |
| let (address, tail) = PS::parse::<[le::U32; 4]>(tail).ok_or(ParseError::MissingData { |
| type_name: "IPv6Node::address", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let num_bytes = tail.len(); |
| let (mask, tail) = PS::parse::<[le::U32; 4]>(tail).ok_or(ParseError::MissingData { |
| type_name: "IPv6Node::mask", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for ipv6 node")?; |
| |
| Ok((Self { address, mask, context }, tail)) |
| } |
| } |
| |
| pub(crate) type InfinitiBandPartitionKeys<PS> = Vec<InfinitiBandPartitionKey<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for InfinitiBandPartitionKeys<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency of sequence of [`InfinitiBandPartitionKey`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct InfinitiBandPartitionKey<PS: ParseStrategy> { |
| low: PS::Output<le::U32>, |
| high: PS::Output<le::U32>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for InfinitiBandPartitionKey<PS> |
| where |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let num_bytes = tail.len(); |
| let (low, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "InfinitiBandPartitionKey::low", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let num_bytes = tail.len(); |
| let (high, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "InfinitiBandPartitionKey::high", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for infiniti band partition key")?; |
| |
| Ok((Self { low, high, context }, tail)) |
| } |
| } |
| |
| impl<PS: ParseStrategy> Validate for InfinitiBandPartitionKey<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate consistency between fields of [`InfinitiBandPartitionKey`]. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| pub(crate) type InfinitiBandEndPorts<PS> = Vec<InfinitiBandEndPort<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for InfinitiBandEndPorts<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`InfinitiBandEndPort`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct InfinitiBandEndPort<PS: ParseStrategy> { |
| port_and_name: Array<PS, PS::Output<InfinitiBandEndPortMetadata>, PS::Slice<u8>>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for InfinitiBandEndPort<PS> |
| where |
| Array<PS, PS::Output<InfinitiBandEndPortMetadata>, PS::Slice<u8>>: Parse<PS>, |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (port_and_name, tail) = |
| Array::<PS, PS::Output<InfinitiBandEndPortMetadata>, PS::Slice<u8>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing infiniti band end port metadata")?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for infiniti band end port")?; |
| |
| Ok((Self { port_and_name, context }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct InfinitiBandEndPortMetadata { |
| length: le::U32, |
| port: le::U32, |
| } |
| |
| impl Counted for InfinitiBandEndPortMetadata { |
| fn count(&self) -> u32 { |
| self.length.get() |
| } |
| } |
| |
| pub(crate) type GenericFsContexts<PS> = Vec<GenericFsContext<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for GenericFsContexts<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`GenericFsContext`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct GenericFsContext<PS: ParseStrategy> { |
| type_name: SimpleArray<PS, PS::Slice<u8>>, |
| contexts: SimpleArray<PS, FsContexts<PS>>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for GenericFsContext<PS> |
| where |
| SimpleArray<PS, PS::Slice<u8>>: Parse<PS>, |
| SimpleArray<PS, FsContexts<PS>>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (type_name, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing generic filesystem context name")?; |
| |
| let (contexts, tail) = SimpleArray::<PS, FsContexts<PS>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing generic filesystem contexts")?; |
| |
| Ok((Self { type_name, contexts }, tail)) |
| } |
| } |
| |
| pub(crate) type FsContexts<PS> = Vec<FsContext<PS>>; |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct FsContext<PS: ParseStrategy> { |
| name: SimpleArray<PS, PS::Slice<u8>>, |
| class: PS::Output<le::U32>, |
| context: Context<PS>, |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for FsContext<PS> |
| where |
| SimpleArray<PS, PS::Slice<u8>>: Parse<PS>, |
| Context<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (name, tail) = SimpleArray::<PS, PS::Slice<u8>>::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing filesystem context name")?; |
| |
| let num_bytes = tail.len(); |
| let (class, tail) = PS::parse::<le::U32>(tail).ok_or(ParseError::MissingData { |
| type_name: "FsContext::class", |
| type_size: std::mem::size_of::<le::U32>(), |
| num_bytes, |
| })?; |
| |
| let (context, tail) = Context::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing context for filesystem context")?; |
| |
| Ok((Self { name, class, context }, tail)) |
| } |
| } |
| |
| pub(crate) type RangeTransitions<PS> = Vec<RangeTransition<PS>>; |
| |
| impl<PS: ParseStrategy> Validate for RangeTransitions<PS> { |
| type Error = anyhow::Error; |
| |
| /// TODO: Validate sequence of [`RangeTransition`] objects. |
| fn validate(&self) -> Result<(), Self::Error> { |
| Ok(()) |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub(crate) struct RangeTransition<PS: ParseStrategy> { |
| metadata: PS::Output<RangeTransitionMetadata>, |
| mls_range: MlsRange<PS>, |
| } |
| |
| impl<PS: ParseStrategy> RangeTransition<PS> { |
| pub fn source_type(&self) -> TypeId { |
| TypeId(NonZeroU32::new(PS::deref(&self.metadata).source_type.get()).unwrap()) |
| } |
| |
| pub fn target_type(&self) -> TypeId { |
| TypeId(NonZeroU32::new(PS::deref(&self.metadata).target_type.get()).unwrap()) |
| } |
| |
| pub fn target_class(&self) -> le::U32 { |
| PS::deref(&self.metadata).target_class |
| } |
| |
| pub fn mls_range(&self) -> &MlsRange<PS> { |
| &self.mls_range |
| } |
| } |
| |
| impl<PS: ParseStrategy> Parse<PS> for RangeTransition<PS> |
| where |
| MlsRange<PS>: Parse<PS>, |
| { |
| type Error = anyhow::Error; |
| |
| fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> { |
| let tail = bytes; |
| |
| let (metadata, tail) = PS::parse::<RangeTransitionMetadata>(tail) |
| .context("parsing range transition metadata")?; |
| |
| let (mls_range, tail) = MlsRange::parse(tail) |
| .map_err(Into::<anyhow::Error>::into) |
| .context("parsing mls range for range transition")?; |
| |
| Ok((Self { metadata, mls_range }, tail)) |
| } |
| } |
| |
| #[derive(Clone, Debug, FromZeroes, FromBytes, NoCell, PartialEq, Unaligned)] |
| #[repr(C, packed)] |
| pub(crate) struct RangeTransitionMetadata { |
| source_type: le::U32, |
| target_type: le::U32, |
| target_class: le::U32, |
| } |