Skip to main content

selinux/policy/
symbols.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
5use super::error::ValidateError;
6use super::extensible_bitmap::ExtensibleBitmap;
7use super::parser::{PolicyCursor, PolicyData, PolicyOffset};
8
9use super::view::U24;
10use super::{
11    Array, CategoryId, Counted, MlsLevel, MlsRange, Parse, PolicyValidationContext, RoleId,
12    SensitivityId, TypeId, UserId, Validate, ValidateArray, array_type,
13    array_type_validate_deref_both,
14};
15
16use crate::new_policy::traits::PolicyId;
17use anyhow::{Context as _, anyhow};
18use hashbrown::hash_table::HashTable;
19use rapidhash::RapidHasher;
20use std::fmt::Debug;
21use std::hash::{Hash, Hasher};
22use std::ops::Deref;
23use zerocopy::{FromBytes, Immutable, KnownLayout, Unaligned, little_endian as le};
24
25/// Exact value of [`Type`] `properties` when the underlying data refers to an SELinux type.
26const TYPE_PROPERTIES_TYPE: u32 = 1;
27
28/// Exact value of [`Type`] `properties` when the underlying data refers to an SELinux alias.
29const TYPE_PROPERTIES_ALIAS: u32 = 0;
30
31/// Exact value of [`Type`] `properties` when the underlying data refers to an SELinux attribute.
32const TYPE_PROPERTIES_ATTRIBUTE: u32 = 3;
33
34/// [`SymbolList`] is an [`Array`] of items with the count of items determined by [`Metadata`] as
35/// [`Counted`].
36#[derive(Debug, PartialEq)]
37pub(super) struct SymbolList<T>(Array<Metadata, T>);
38
39impl<T> Deref for SymbolList<T> {
40    type Target = Array<Metadata, T>;
41
42    fn deref(&self) -> &Self::Target {
43        &self.0
44    }
45}
46
47impl<T: Parse> Parse for SymbolList<T> {
48    type Error = <Array<Metadata, T> as Parse>::Error;
49
50    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
51        let (array, tail) = Array::<Metadata, T>::parse(bytes)?;
52        Ok((Self(array), tail))
53    }
54}
55
56impl<T: Validate> Validate for SymbolList<T> {
57    type Error = anyhow::Error;
58
59    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
60        self.0.metadata.validate(context)?;
61        self.0.data.validate(context).map_err(Into::<anyhow::Error>::into)?;
62
63        Ok(())
64    }
65}
66
67/// Binary metadata prefix to [`SymbolList`] objects.
68#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
69#[repr(C, packed)]
70pub(super) struct Metadata {
71    /// The number of primary names referred to in the associated [`SymbolList`].
72    primary_names_count: le::U32,
73    /// The number of objects in the associated [`SymbolList`] [`Array`].
74    count: le::U32,
75}
76
77impl Metadata {
78    pub fn primary_names_count(&self) -> u32 {
79        self.primary_names_count.get()
80    }
81}
82
83impl Counted for Metadata {
84    /// The number of items that follow a [`Metadata`] is the value stored in the `metadata.count`
85    /// field.
86    fn count(&self) -> u32 {
87        self.count.get()
88    }
89}
90
91impl Validate for Metadata {
92    type Error = anyhow::Error;
93
94    /// TODO: Should there be an upper bound on `primary_names_count` or `count`?
95    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
96        Ok(())
97    }
98}
99
100impl Validate for Role {
101    type Error = anyhow::Error;
102
103    /// TODO: Validate [`Role`].
104    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
105        Ok(())
106    }
107}
108
109#[derive(Debug, PartialEq)]
110pub(super) struct Role {
111    metadata: RoleMetadata,
112    role_dominates: ExtensibleBitmap,
113    role_types: ExtensibleBitmap,
114}
115
116impl Role {
117    pub(super) fn id(&self) -> RoleId {
118        RoleId::from_u32(self.metadata.metadata.id.get()).unwrap()
119    }
120
121    pub(super) fn name_bytes(&self) -> &[u8] {
122        &self.metadata.data
123    }
124
125    pub(super) fn types(&self) -> &ExtensibleBitmap {
126        &self.role_types
127    }
128}
129
130impl Parse for Role {
131    type Error = anyhow::Error;
132
133    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
134        let tail = bytes;
135
136        let (metadata, tail) = RoleMetadata::parse(tail)
137            .map_err(Into::<anyhow::Error>::into)
138            .context("parsing role metadata")?;
139
140        let (role_dominates, tail) = ExtensibleBitmap::parse(tail)
141            .map_err(Into::<anyhow::Error>::into)
142            .context("parsing role dominates")?;
143
144        let (role_types, tail) = ExtensibleBitmap::parse(tail)
145            .map_err(Into::<anyhow::Error>::into)
146            .context("parsing role types")?;
147
148        Ok((Self { metadata, role_dominates, role_types }, tail))
149    }
150}
151
152array_type!(RoleMetadata, RoleStaticMetadata, u8);
153
154array_type_validate_deref_both!(RoleMetadata);
155
156impl ValidateArray<RoleStaticMetadata, u8> for RoleMetadata {
157    type Error = anyhow::Error;
158
159    /// [`RoleMetadata`] has no internal constraints beyond those imposed by [`Array`].
160    fn validate_array(
161        _context: &PolicyValidationContext,
162        _metadata: &RoleStaticMetadata,
163        _items: &[u8],
164    ) -> Result<(), Self::Error> {
165        Ok(())
166    }
167}
168
169#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
170#[repr(C, packed)]
171pub(super) struct RoleStaticMetadata {
172    length: le::U32,
173    id: le::U32,
174    bounds: le::U32,
175}
176
177impl Counted for RoleStaticMetadata {
178    /// [`RoleStaticMetadata`] serves as [`Counted`] for a length-encoded `[u8]`.
179    fn count(&self) -> u32 {
180        self.length.get()
181    }
182}
183
184impl Validate for RoleStaticMetadata {
185    type Error = anyhow::Error;
186
187    /// TODO: Should there be any constraints on `length`, `value`, or `bounds`?
188    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
189        Ok(())
190    }
191}
192
193array_type!(Type, TypeMetadata, u8);
194
195array_type_validate_deref_both!(Type);
196
197impl Type {
198    /// Returns the name of this type.
199    pub fn name_bytes(&self) -> &[u8] {
200        &self.data
201    }
202
203    /// Returns the id associated with this type. The id is used to index into collections and
204    /// bitmaps associated with this type. The id is 1-indexed, whereas most collections and
205    /// bitmaps are 0-indexed, so clients of this API will usually use `id - 1`.
206    pub fn id(&self) -> TypeId {
207        TypeId::from_u32(self.metadata.id.get()).unwrap()
208    }
209
210    /// Returns the Id of the bounding type, if any.
211    pub fn bounded_by(&self) -> Option<TypeId> {
212        TypeId::from_u32(self.metadata.bounds.get())
213    }
214}
215
216impl ValidateArray<TypeMetadata, u8> for Type {
217    type Error = anyhow::Error;
218
219    /// TODO: Validate that `PS::deref(&self.data)` is an ascii string that contains a valid type name.
220    fn validate_array(
221        _context: &PolicyValidationContext,
222        _metadata: &TypeMetadata,
223        _items: &[u8],
224    ) -> Result<(), Self::Error> {
225        Ok(())
226    }
227}
228
229#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
230#[repr(C, packed)]
231pub(super) struct TypeMetadata {
232    length: le::U32,
233    id: le::U32,
234    properties: le::U32,
235    bounds: le::U32,
236}
237
238impl Counted for TypeMetadata {
239    fn count(&self) -> u32 {
240        self.length.get()
241    }
242}
243
244impl Validate for TypeMetadata {
245    type Error = anyhow::Error;
246
247    /// TODO: Validate [`TypeMetadata`] internals.
248    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
249        Ok(())
250    }
251}
252
253fn name_hash(name: &[u8]) -> u64 {
254    let mut hasher = RapidHasher::default();
255    name.hash(&mut hasher);
256    hasher.finish()
257}
258
259#[derive(Debug)]
260pub(super) struct TypeIndex {
261    // TODO: https://fxbug.dev/483930877 - we don't use or need this after validation; it
262    // would be nice to avoid having it continue to be stored here after its last use.
263    primary_names_count: u32,
264
265    /// A mapping from [`TypeId`] (represented as position-in-the-array-plus-one) to
266    /// the corresponding [`Type`] (represented as an offset into the policy bytes).
267    /// If zero is the value at index `i` of this structure, that indicates that the
268    /// binary policy has no type with type ID `i + 1`. Only types ([`Type`]s matching
269    /// `TYPE_PROPERTIES_TYPE`) and attributes ([`Type`]s matching
270    /// `TYPE_PROPERTIES_ATTRIBUTE`) are included in this structure; type aliases are
271    /// excluded (were we to want to include them, they would "claim" the ID properly
272    /// belonging to exactly one non-alias [`Type`]).
273    //
274    // TODO: https://fxbug.dev/479180246 - we currently allow for "holes" (integer type IDs
275    // that do not correspond to any type) in this array, but do we need to? Will all the
276    // binary policies that we encounter be "packed" such that they use every integer
277    // between one and the largest integer that they use?
278    offsets_by_id_minus_one: Box<[U24]>,
279
280    /// A mapping from the string name of a [`Type`] to that type's location in the policy
281    /// bytes. This structure contains entries for all types ([`Type`]s matching
282    /// `TYPE_PROPERTIES_TYPE`) and type aliases ([`Type`]s matching
283    /// `TYPE_PROPERTIES_ALIAS`) but not attributes ([`Type`]s matching
284    /// `TYPE_PROPERTIES_ATTRIBUTE`); attributes are never looked up by name.
285    offsets_by_name: HashTable<U24>,
286}
287
288impl TypeIndex {
289    fn parse_type_at(policy_bytes: &PolicyData, offset: U24) -> Type {
290        Type::parse(PolicyCursor::new_at(policy_bytes, PolicyOffset::from(offset)))
291            .expect("These bytes already successfully parsed")
292            .0
293    }
294
295    pub(super) fn primary_names_count(&self) -> u32 {
296        self.primary_names_count
297    }
298
299    pub(super) fn type_id_by_name(&self, name: &str, data: &PolicyData) -> Option<TypeId> {
300        let name_bytes = name.as_bytes();
301        self.offsets_by_name
302            .find(name_hash(name_bytes), |&other_offset| {
303                Self::parse_type_at(data, other_offset).name_bytes() == name_bytes
304            })
305            .map(|&offset| Self::parse_type_at(data, offset).id())
306    }
307
308    pub(super) fn type_by_type_id(&self, id: TypeId, data: &PolicyData) -> Type {
309        Self::parse_type_at(data, self.offsets_by_id_minus_one[(id.as_u32() - 1) as usize])
310    }
311
312    /// Returns an iterator over all the type-Ids, for use by the post-parse validation.
313    pub(super) fn all_type_ids(&self) -> impl Iterator<Item = TypeId> {
314        self.offsets_by_id_minus_one.iter().enumerate().filter_map(
315            |(index, offset)| match u32::from(*offset) {
316                0 => None,
317                _ => Some(TypeId::from_u32((index + 1) as u32).unwrap()),
318            },
319        )
320    }
321}
322
323impl Parse for TypeIndex {
324    type Error = anyhow::Error;
325
326    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
327        let policy_data = bytes.data();
328        let (metadata, mut tail) = Metadata::parse(bytes)?;
329        let type_count = usize::try_from(metadata.count()).unwrap();
330        let mut offsets_by_id_minus_one = Vec::with_capacity(type_count);
331        let mut offsets_by_name = HashTable::with_capacity(type_count);
332        for _ in 0..type_count {
333            let offset = U24::try_from(tail.offset()).unwrap();
334            let (type_, next_tail) = Type::parse(tail)?;
335
336            let will_be_looked_up_by_id;
337            let will_be_looked_up_by_name;
338            match type_.metadata.properties.get() {
339                TYPE_PROPERTIES_TYPE => {
340                    will_be_looked_up_by_id = true;
341                    will_be_looked_up_by_name = true;
342                }
343                TYPE_PROPERTIES_ATTRIBUTE => {
344                    will_be_looked_up_by_id = true;
345                    will_be_looked_up_by_name = false;
346                }
347                TYPE_PROPERTIES_ALIAS => {
348                    will_be_looked_up_by_id = false;
349                    will_be_looked_up_by_name = true;
350                }
351                unrecognized => {
352                    return Err(anyhow!(
353                        "Can't parse \"type\" element with \"properties\" value {:?}",
354                        unrecognized
355                    ));
356                }
357            }
358
359            if will_be_looked_up_by_id {
360                let type_id_as_usize = type_.id().as_u32() as usize;
361                if offsets_by_id_minus_one.len() < type_id_as_usize {
362                    offsets_by_id_minus_one.resize(type_id_as_usize, U24::try_from(0).unwrap());
363                }
364                offsets_by_id_minus_one[type_id_as_usize - 1] = offset;
365            }
366
367            if will_be_looked_up_by_name {
368                let name_bytes = type_.name_bytes();
369                offsets_by_name
370                    .entry(
371                        name_hash(name_bytes),
372                        |&other_offset| {
373                            Self::parse_type_at(&policy_data, other_offset).name_bytes()
374                                == name_bytes
375                        },
376                        |&other_offset| {
377                            name_hash(Self::parse_type_at(&policy_data, other_offset).name_bytes())
378                        },
379                    )
380                    .insert(offset);
381            }
382
383            tail = next_tail;
384        }
385        let offsets_by_id_minus_one = Box::<[U24]>::from(offsets_by_id_minus_one);
386        offsets_by_name.shrink_to_fit(|&other_offset| {
387            name_hash(Self::parse_type_at(&policy_data, other_offset).name_bytes())
388        });
389
390        Ok((
391            Self {
392                primary_names_count: metadata.primary_names_count(),
393                offsets_by_id_minus_one,
394                offsets_by_name,
395            },
396            tail,
397        ))
398    }
399}
400
401impl Validate for TypeIndex {
402    type Error = anyhow::Error;
403
404    /// TODO: Validate internal consistency between consecutive [`Type`] instances.
405    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
406        let data = context.data.clone();
407        let mut primary_names_count = 0u32;
408        for offset in &self.offsets_by_id_minus_one {
409            if PolicyOffset::from(*offset) != 0 {
410                Self::parse_type_at(&data, *offset).validate(context)?;
411                primary_names_count += 1;
412            }
413        }
414
415        if self.primary_names_count != primary_names_count {
416            return Err(anyhow!(
417                "Expected {:?} primary names but found {:?}",
418                self.primary_names_count,
419                primary_names_count
420            ));
421        }
422
423        Ok(())
424    }
425}
426
427impl Validate for User {
428    type Error = anyhow::Error;
429
430    /// TODO: Validate [`User`].
431    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
432        Ok(())
433    }
434}
435
436#[derive(Debug, PartialEq)]
437pub(super) struct User {
438    user_data: UserData,
439    roles: ExtensibleBitmap,
440    expanded_range: MlsRange,
441    default_level: MlsLevel,
442}
443
444impl User {
445    pub(super) fn id(&self) -> UserId {
446        UserId::from_u32(self.user_data.metadata.id.get()).unwrap()
447    }
448
449    pub(super) fn name_bytes(&self) -> &[u8] {
450        &self.user_data.data
451    }
452
453    pub(super) fn roles(&self) -> &ExtensibleBitmap {
454        &self.roles
455    }
456
457    pub(super) fn mls_range(&self) -> &MlsRange {
458        &self.expanded_range
459    }
460}
461
462impl Parse for User {
463    type Error = anyhow::Error;
464
465    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
466        let tail = bytes;
467
468        let (user_data, tail) = UserData::parse(tail)
469            .map_err(Into::<anyhow::Error>::into)
470            .context("parsing user data")?;
471
472        let (roles, tail) = ExtensibleBitmap::parse(tail)
473            .map_err(Into::<anyhow::Error>::into)
474            .context("parsing user roles")?;
475
476        let (expanded_range, tail) =
477            MlsRange::parse(tail).context("parsing user expanded range")?;
478
479        let (default_level, tail) = MlsLevel::parse(tail).context("parsing user default level")?;
480
481        Ok((Self { user_data, roles, expanded_range, default_level }, tail))
482    }
483}
484
485array_type!(UserData, UserMetadata, u8);
486
487array_type_validate_deref_both!(UserData);
488
489impl ValidateArray<UserMetadata, u8> for UserData {
490    type Error = anyhow::Error;
491
492    /// TODO: Validate consistency between [`UserMetadata`] in `self.metadata` and `[u8]` key in `self.data`.
493    fn validate_array(
494        _context: &PolicyValidationContext,
495        _metadata: &UserMetadata,
496        _items: &[u8],
497    ) -> Result<(), Self::Error> {
498        Ok(())
499    }
500}
501
502#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
503#[repr(C, packed)]
504pub(super) struct UserMetadata {
505    length: le::U32,
506    id: le::U32,
507    bounds: le::U32,
508}
509
510impl Counted for UserMetadata {
511    fn count(&self) -> u32 {
512        self.length.get()
513    }
514}
515
516impl Validate for UserMetadata {
517    type Error = anyhow::Error;
518
519    /// TODO: Validate [`UserMetadata`] internals.
520    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
521        Ok(())
522    }
523}
524
525impl Parse for MlsLevel {
526    type Error = anyhow::Error;
527
528    fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
529        let offset = cursor.offset() as usize;
530        let slice = &cursor.data().as_ref()[offset..];
531        let mut new_cursor = crate::new_policy::parser::PolicyCursor::new(slice);
532        let level = <Self as crate::new_policy::traits::Parse>::parse(&mut new_cursor)
533            .map_err(|e| anyhow::anyhow!("Parse error: {:?}", e))?;
534        let bytes_parsed = new_cursor.offset();
535        let new_offset = cursor.offset() + bytes_parsed as u32;
536        Ok((level, PolicyCursor::new_at(cursor.data(), new_offset)))
537    }
538}
539
540impl Validate for MlsLevel {
541    type Error = anyhow::Error;
542
543    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
544        crate::new_policy::traits::Validate::validate(self, &context.new_policy).map_err(Into::into)
545    }
546}
547
548impl Parse for MlsRange {
549    type Error = anyhow::Error;
550
551    fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
552        let offset = cursor.offset() as usize;
553        let slice = &cursor.data().as_ref()[offset..];
554        let mut new_cursor = crate::new_policy::parser::PolicyCursor::new(slice);
555        let range = <Self as crate::new_policy::traits::Parse>::parse(&mut new_cursor)
556            .map_err(|e| anyhow::anyhow!("Parse error: {:?}", e))?;
557        let bytes_parsed = new_cursor.offset();
558        let new_offset = cursor.offset() + bytes_parsed as u32;
559        Ok((range, PolicyCursor::new_at(cursor.data(), new_offset)))
560    }
561}
562
563impl Validate for MlsRange {
564    type Error = anyhow::Error;
565
566    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
567        crate::new_policy::traits::Validate::validate(self, &context.new_policy).map_err(Into::into)
568    }
569}
570
571array_type!(ConditionalBoolean, ConditionalBooleanMetadata, u8);
572
573array_type_validate_deref_both!(ConditionalBoolean);
574
575impl ValidateArray<ConditionalBooleanMetadata, u8> for ConditionalBoolean {
576    type Error = anyhow::Error;
577
578    /// TODO: Validate consistency between [`ConditionalBooleanMetadata`] and `[u8]` key.
579    fn validate_array(
580        _context: &PolicyValidationContext,
581        _metadata: &ConditionalBooleanMetadata,
582        _items: &[u8],
583    ) -> Result<(), Self::Error> {
584        Ok(())
585    }
586}
587
588#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
589#[repr(C, packed)]
590pub(super) struct ConditionalBooleanMetadata {
591    id: le::U32,
592    /// Current active value of this conditional boolean.
593    active: le::U32,
594    length: le::U32,
595}
596
597impl ConditionalBooleanMetadata {
598    /// Returns the active value for the boolean.
599    pub(super) fn active(&self) -> bool {
600        self.active != le::U32::ZERO
601    }
602}
603
604impl Counted for ConditionalBooleanMetadata {
605    /// [`ConditionalBooleanMetadata`] used as `M` in of `Array<PS, PS::Output<M>, PS::Slice<u8>>` with
606    /// `self.length` denoting size of inner `[u8]`.
607    fn count(&self) -> u32 {
608        self.length.get()
609    }
610}
611
612impl Validate for ConditionalBooleanMetadata {
613    type Error = anyhow::Error;
614
615    /// TODO: Validate internal consistency of [`ConditionalBooleanMetadata`].
616    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
617        Ok(())
618    }
619}
620
621#[derive(Debug, PartialEq)]
622pub(super) struct Sensitivity {
623    metadata: SensitivityMetadata,
624    level: MlsLevel,
625}
626
627impl Sensitivity {
628    pub fn id(&self) -> SensitivityId {
629        self.level.sensitivity()
630    }
631
632    pub fn name_bytes(&self) -> &[u8] {
633        &self.metadata.data
634    }
635}
636
637impl Parse for Sensitivity {
638    type Error = anyhow::Error;
639
640    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
641        let tail = bytes;
642
643        let (metadata, tail) = SensitivityMetadata::parse(tail)
644            .map_err(Into::<anyhow::Error>::into)
645            .context("parsing sensitivity metadata")?;
646
647        let (level, tail) = MlsLevel::parse(tail)
648            .map_err(Into::<anyhow::Error>::into)
649            .context("parsing sensitivity mls level")?;
650
651        Ok((Self { metadata, level }, tail))
652    }
653}
654
655impl Validate for Sensitivity {
656    type Error = anyhow::Error;
657
658    /// TODO: Validate internal consistency of `self.metadata` and `self.level`.
659    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
660        Ok(())
661    }
662}
663
664array_type!(SensitivityMetadata, SensitivityStaticMetadata, u8);
665
666array_type_validate_deref_both!(SensitivityMetadata);
667
668impl ValidateArray<SensitivityStaticMetadata, u8> for SensitivityMetadata {
669    type Error = anyhow::Error;
670
671    /// TODO: Validate consistency between [`SensitivityMetadata`] and `[u8]` key.
672    fn validate_array(
673        _context: &PolicyValidationContext,
674        _metadata: &SensitivityStaticMetadata,
675        _items: &[u8],
676    ) -> Result<(), Self::Error> {
677        Ok(())
678    }
679}
680
681#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
682#[repr(C, packed)]
683pub(super) struct SensitivityStaticMetadata {
684    length: le::U32,
685    is_alias: le::U32,
686}
687
688impl Counted for SensitivityStaticMetadata {
689    /// [`SensitivityStaticMetadata`] used as `M` in of `Array<PS, PS::Output<M>, PS::Slice<u8>>` with
690    /// `self.length` denoting size of inner `[u8]`.
691    fn count(&self) -> u32 {
692        self.length.get()
693    }
694}
695
696impl Validate for SensitivityStaticMetadata {
697    type Error = anyhow::Error;
698
699    /// TODO: Validate internal consistency of [`SensitivityStaticMetadata`].
700    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
701        Ok(())
702    }
703}
704
705array_type!(Category, CategoryMetadata, u8);
706
707array_type_validate_deref_both!(Category);
708
709impl Category {
710    pub fn id(&self) -> CategoryId {
711        CategoryId::from_u32(self.metadata.id.get()).unwrap()
712    }
713
714    pub fn name_bytes(&self) -> &[u8] {
715        &self.data
716    }
717}
718
719impl ValidateArray<CategoryMetadata, u8> for Category {
720    type Error = anyhow::Error;
721
722    /// TODO: Validate consistency between [`CategoryMetadata`] and `[u8]` key.
723    fn validate_array(
724        _context: &PolicyValidationContext,
725        _metadata: &CategoryMetadata,
726        _items: &[u8],
727    ) -> Result<(), Self::Error> {
728        Ok(())
729    }
730}
731
732#[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
733#[repr(C, packed)]
734pub(super) struct CategoryMetadata {
735    length: le::U32,
736    id: le::U32,
737    is_alias: le::U32,
738}
739
740impl Counted for CategoryMetadata {
741    /// [`CategoryMetadata`] used as `M` in of `Array<PS, PS::Output<M>, PS::Slice<u8>>` with
742    /// `self.length` denoting size of inner `[u8]`.
743    fn count(&self) -> u32 {
744        self.length.get()
745    }
746}
747
748impl Validate for CategoryMetadata {
749    type Error = anyhow::Error;
750
751    /// TODO: Validate internal consistency of [`CategoryMetadata`].
752    fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
753        CategoryId::from_u32(self.id.get()).ok_or(ValidateError::NonOptionalIdIsZero)?;
754        Ok(())
755    }
756}
757
758#[derive(Debug)]
759pub(super) struct CategoryIndex {
760    /// A mapping from [`CategoryId`] (represented as position-in-the-array-plus-one)
761    /// to the corresponding [`Category`] (represented as an offset into the policy
762    /// bytes). If zero is the value at index `i` of this structure, that indicates that
763    /// the policy bytes have no category with ID `i + 1`.
764    //
765    // TODO: https://fxbug.dev/479180246 - we currently allow for "holes" (integer
766    // category IDs that do not correspond to any category) in this array, but do we
767    // need to? Will all the binary policies that we encounter be "packed" such that
768    // they use every integer between one and the largest integer that they use?
769    offsets_by_id_minus_one: Box<[U24]>,
770
771    /// A mapping from category name hash to the offset of that category in the policy.
772    offsets_by_name: HashTable<U24>,
773}
774
775impl CategoryIndex {
776    fn parse_category_at(policy_bytes: &PolicyData, offset: U24) -> Category {
777        Category::parse(PolicyCursor::new_at(policy_bytes, PolicyOffset::from(offset)))
778            .expect("These bytes already successfully parsed")
779            .0
780    }
781
782    /// Looks up a [`Category`] by its [`CategoryId`].
783    pub fn category(&self, policy_bytes: &PolicyData, category_id: CategoryId) -> Category {
784        let offset = self.offsets_by_id_minus_one[(category_id.as_u32() - 1) as usize];
785        Self::parse_category_at(policy_bytes, offset)
786    }
787
788    /// Looks up all [`Category`]s given in the policy. This is linear in time and
789    /// space and inappropriate to call in from a performance-sensitive context, but
790    /// may be called during policy parsing/validation, selinuxfs file operations, and
791    /// filesystem extended attribute value calculations.
792    pub fn categories<'a>(
793        &'a self,
794        policy_bytes: &'a PolicyData,
795    ) -> impl Iterator<Item = Category> + 'a {
796        self.offsets_by_id_minus_one.iter().filter_map(|&offset| match PolicyOffset::from(offset) {
797            0 => None,
798            offset => {
799                let (category, _) =
800                    Category::parse(PolicyCursor::new_at(policy_bytes, PolicyOffset::from(offset)))
801                        .expect("These bytes already successfully parsed");
802                Some(category)
803            }
804        })
805    }
806
807    /// Looks up a [`Category`] by its name in constant time.
808    pub fn category_by_name(&self, policy_bytes: &PolicyData, name: &str) -> Option<Category> {
809        let name_bytes = name.as_bytes();
810        self.offsets_by_name
811            .find(name_hash(name_bytes), |&other_offset| {
812                name_bytes == Self::parse_category_at(policy_bytes, other_offset).name_bytes()
813            })
814            .map(|&offset| Self::parse_category_at(policy_bytes, offset))
815    }
816}
817
818impl Parse for CategoryIndex {
819    type Error = anyhow::Error;
820
821    fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
822        let policy_data = bytes.data();
823        let (metadata, mut tail) = Metadata::parse(bytes)?;
824        let category_count = usize::try_from(metadata.count()).unwrap();
825        let mut offsets_by_id_minus_one = vec![U24::ZERO; category_count];
826        let mut offsets_by_name = HashTable::with_capacity(category_count);
827
828        for _ in 0..category_count {
829            let offset = U24::try_from(tail.offset()).unwrap();
830            let (category, next_tail) = Category::parse(tail)?;
831            let category_id_as_usize = category.id().as_u32() as usize;
832
833            if offsets_by_id_minus_one.len() < category_id_as_usize {
834                offsets_by_id_minus_one.resize(category_id_as_usize, U24::ZERO);
835            }
836            offsets_by_id_minus_one[category_id_as_usize - 1] = offset;
837
838            offsets_by_name
839                .entry(
840                    name_hash(category.name_bytes()),
841                    |&other_offset| {
842                        category.name_bytes()
843                            == Self::parse_category_at(&policy_data, other_offset).name_bytes()
844                    },
845                    |&other_offset| {
846                        name_hash(Self::parse_category_at(&policy_data, other_offset).name_bytes())
847                    },
848                )
849                .insert(offset);
850
851            tail = next_tail;
852        }
853
854        offsets_by_name.shrink_to_fit(|&other_offset| {
855            name_hash(Self::parse_category_at(&policy_data, other_offset).name_bytes())
856        });
857
858        Ok((
859            Self {
860                offsets_by_id_minus_one: Box::<[U24]>::from(offsets_by_id_minus_one),
861                offsets_by_name,
862            },
863            tail,
864        ))
865    }
866}
867
868impl Validate for CategoryIndex {
869    type Error = anyhow::Error;
870
871    /// TODO: Validate consistency of sequence of [`Category`].
872    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
873        for offset in &self.offsets_by_id_minus_one {
874            let (category, _) =
875                Category::parse(PolicyCursor::new_at(&context.data, PolicyOffset::from(*offset)))
876                    .expect("These bytes already successfully parsed");
877
878            category.validate(context).context("category defaults")?;
879        }
880
881        Ok(())
882    }
883}
884
885#[cfg(test)]
886mod tests {
887    use super::super::{AccessVector, CategoryId, SensitivityId, UserId, parse_policy_by_value};
888
889    use crate::new_policy::{
890        ConstraintNames, ConstraintOperand, ConstraintOperator, ConstraintSubject, ConstraintTerm,
891    };
892
893    #[test]
894    fn mls_levels_for_user_context() {
895        const TEST_POLICY: &[u8] =
896            include_bytes! {"../../testdata/micro_policies/multiple_levels_and_categories_policy"};
897        let policy = parse_policy_by_value(TEST_POLICY.to_vec()).unwrap();
898        let policy = policy.validate().unwrap();
899
900        let user = policy.user(UserId::for_test(1));
901        let mls_range = user.mls_range();
902        let low_level = mls_range.low();
903        let high_level = mls_range.high().as_ref().expect("user 1 has a high mls level");
904
905        assert_eq!(low_level.sensitivity(), SensitivityId::for_test(1));
906        assert_eq!(low_level.category_ids().collect::<Vec<_>>(), vec![CategoryId::for_test(1)]);
907
908        assert_eq!(high_level.sensitivity(), SensitivityId::for_test(2));
909        assert_eq!(
910            high_level.category_ids().collect::<Vec<_>>(),
911            vec![
912                CategoryId::for_test(1),
913                CategoryId::for_test(2),
914                CategoryId::for_test(3),
915                CategoryId::for_test(4),
916                CategoryId::for_test(5),
917            ]
918        );
919    }
920
921    #[test]
922    fn parse_mls_constrain_statement() {
923        let policy_bytes = include_bytes!("../../testdata/micro_policies/constraints_policy");
924        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
925        let policy = policy.validate().expect("validate policy");
926
927        let classes = policy.classes();
928        let class = classes.get_by_name(b"class_mls_constraints").expect("look up class");
929        let constraints = class.constraints();
930        assert_eq!(constraints.len(), 6);
931
932        let expected = [
933            ConstraintTerm::Expression {
934                operand: ConstraintOperand::L1H1,
935                operator: ConstraintOperator::Incomp,
936            },
937            ConstraintTerm::Expression {
938                operand: ConstraintOperand::H1H2,
939                operator: ConstraintOperator::Incomp,
940            },
941            ConstraintTerm::Expression {
942                operand: ConstraintOperand::L1H2,
943                operator: ConstraintOperator::DomBy,
944            },
945            ConstraintTerm::Expression {
946                operand: ConstraintOperand::H1L2,
947                operator: ConstraintOperator::Dom,
948            },
949            ConstraintTerm::Expression {
950                operand: ConstraintOperand::L2H2,
951                operator: ConstraintOperator::Ne,
952            },
953            ConstraintTerm::Expression {
954                operand: ConstraintOperand::L1L2,
955                operator: ConstraintOperator::Eq,
956            },
957        ];
958        for (i, constraint) in constraints.iter().enumerate() {
959            assert_eq!(constraint.access_vector(), AccessVector::from(1), "constraint {}", i);
960            let terms = constraint.constraint_expr();
961            assert_eq!(terms.len(), 1, "constraint {}", i);
962            assert_eq!(terms[0], expected[i], "constraint {}", i);
963        }
964    }
965
966    #[test]
967    fn parse_constrain_statement() {
968        let policy_bytes = include_bytes!("../../testdata/micro_policies/constraints_policy");
969        let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
970        let policy = policy.validate().expect("validate policy");
971
972        let classes = policy.classes();
973        let class = classes.get_by_name(b"class_constraint_nested").expect("look up class");
974        let constraints = class.constraints();
975        assert_eq!(constraints.len(), 1);
976        let constraint = &constraints[0];
977        assert_eq!(constraint.access_vector(), AccessVector::from(1));
978        let terms = constraint.constraint_expr();
979        assert_eq!(terms.len(), 8);
980
981        assert_eq!(terms[7], ConstraintTerm::Or);
982        assert_eq!(terms[6], ConstraintTerm::And);
983        assert_eq!(terms[5], ConstraintTerm::Not);
984
985        assert_eq!(
986            terms[4],
987            ConstraintTerm::Expression {
988                operand: ConstraintOperand::Type(ConstraintSubject::Source),
989                operator: ConstraintOperator::Eq,
990            }
991        );
992
993        assert_eq!(
994            terms[3],
995            ConstraintTerm::Expression {
996                operand: ConstraintOperand::User(ConstraintSubject::Source),
997                operator: ConstraintOperator::Eq,
998            }
999        );
1000
1001        assert_eq!(terms[2], ConstraintTerm::And);
1002
1003        assert_eq!(
1004            terms[1],
1005            ConstraintTerm::Expression {
1006                operand: ConstraintOperand::Role(ConstraintSubject::Source),
1007                operator: ConstraintOperator::Eq,
1008            }
1009        );
1010
1011        match &terms[0] {
1012            ConstraintTerm::ExpressionWithNames { operand, operator, names } => {
1013                assert_eq!(operand, &ConstraintOperand::User(ConstraintSubject::Target));
1014                assert_eq!(operator, &ConstraintOperator::Eq);
1015                match &**names {
1016                    ConstraintNames::Users(ids, set) => {
1017                        assert!(!ids.is_empty());
1018                        assert!(set.is_empty());
1019                    }
1020                    _ => panic!("expected Users"),
1021                }
1022            }
1023            _ => panic!("expected ExpressionWithNames"),
1024        }
1025    }
1026}