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