Skip to main content

selinux/policy/
arrays.rs

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