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