Skip to main content

selinux/policy/
rules.rs

1// Copyright 2026 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 std::hash::Hash;
6use std::num::NonZeroU32;
7use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign};
8use std::sync::atomic::{AtomicU64, Ordering};
9
10use hashbrown::HashTable;
11use hashbrown::hash_table::Entry;
12use selinux_policy_derive::{Parse, Serialize, Validate};
13
14use super::error::{ParseError, SerializeError, ValidateError};
15use super::parser::{Array, PolicyCursor, PolicyWriter};
16use super::traits::{Parse, PolicyId, Serialize, Validate};
17use super::{AccessVector, ClassId, ConditionalBooleanId, Policy, TypeId, U24Index};
18
19/// Flag bit for standard `allow` rules.
20const AV_ALLOW_RULE_FLAG: u16 = 0x1;
21/// Flag bit for `auditallow` rules.
22const AV_AUDITALLOW_RULE_FLAG: u16 = 0x2;
23/// Flag bit for `dontaudit` rules.
24const AV_DONTAUDIT_RULE_FLAG: u16 = 0x4;
25
26/// Flag bit for `type_transition` rules.
27const AV_TYPE_TRANSITION_RULE_FLAG: u16 = 0x10;
28/// Flag bit for `type_member` rules.
29const AV_TYPE_MEMBER_RULE_FLAG: u16 = 0x20;
30/// Flag bit for `type_change` rules.
31const AV_TYPE_CHANGE_RULE_FLAG: u16 = 0x40;
32
33/// Flag bit for `allowxperm` extended permissions rules.
34const AV_ALLOWXPERM_RULE_FLAG: u16 = 0x100;
35/// Flag bit for `auditallowxperm` extended permissions rules.
36const AV_AUDITALLOWXPERM_RULE_FLAG: u16 = 0x200;
37/// Flag bit for `dontauditxperm` extended permissions rules.
38const AV_DONTAUDITXPERM_RULE_FLAG: u16 = 0x400;
39
40/// Mask for high bit in rule type flags indicating whether rule is enabled.
41const AV_ENABLED_RULE_FLAG: u16 = 0x8000;
42
43/// [`AccessDecision::flags`] value indicating that policy marks source domain permissive.
44pub const SELINUX_AVD_FLAGS_PERMISSIVE: u32 = 1;
45
46/// Extended permissions type for ioctl driver prefix and 8-bit postfix sets.
47pub const XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES: u8 = 1;
48/// Extended permissions type for ioctl 8-bit driver prefixes.
49pub const XPERMS_TYPE_IOCTL_PREFIXES: u8 = 2;
50/// Extended permissions type for netlink message types.
51pub const XPERMS_TYPE_NLMSG: u8 = 3;
52
53/// Number of 64-bit words in 256-bit [`XpermsBitmap`].
54const XPERMS_BITMAP_BLOCKS: usize = 4;
55
56/// 256-bit bitmap used for extended permissions (such as ioctls and netlink messages).
57#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub struct XpermsBitmap([u64; XPERMS_BITMAP_BLOCKS]);
59
60impl Parse for XpermsBitmap {
61    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
62        let mut words = [0u64; XPERMS_BITMAP_BLOCKS];
63        for word in words.iter_mut() {
64            let low = cursor.parse::<u32>()? as u64;
65            let high = cursor.parse::<u32>()? as u64;
66            *word = low | (high << 32);
67        }
68        Ok(Self(words))
69    }
70}
71
72impl Serialize for XpermsBitmap {
73    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
74        for &word in self.0.iter() {
75            (word as u32).serialize(writer)?;
76            ((word >> 32) as u32).serialize(writer)?;
77        }
78        Ok(())
79    }
80}
81
82impl XpermsBitmap {
83    pub const BITMAP_BLOCKS: usize = XPERMS_BITMAP_BLOCKS;
84    /// Bitmap with all 256 bits set to 1.
85    pub const ALL: Self = Self([u64::MAX; Self::BITMAP_BLOCKS]);
86    /// Empty bitmap with all bits set to 0.
87    pub const NONE: Self = Self([0u64; Self::BITMAP_BLOCKS]);
88
89    /// Constructs a new [`XpermsBitmap`] from an array of four 64-bit words.
90    #[cfg(test)]
91    pub fn new(elements: [u64; Self::BITMAP_BLOCKS]) -> Self {
92        Self(elements)
93    }
94
95    /// Returns `true` if the bit corresponding to `value` is set in this bitmap.
96    pub fn contains(&self, value: u8) -> bool {
97        let block_index = (value as usize) / (u64::BITS as usize);
98        let bit_index = (value as usize) % (u64::BITS as usize);
99        self.0[block_index] & (1u64 << bit_index) != 0
100    }
101
102    /// Constructs an [`XpermsBitmap`] by loading words from an array of atomic 64-bit integers using relaxed ordering.
103    pub fn from_atomics(atomics: &[AtomicU64; Self::BITMAP_BLOCKS]) -> Self {
104        let mut words = [0u64; Self::BITMAP_BLOCKS];
105        for (i, word) in words.iter_mut().enumerate() {
106            *word = atomics[i].load(Ordering::Relaxed);
107        }
108        Self(words)
109    }
110
111    /// Stores this bitmap into an array of atomic 64-bit integers using relaxed ordering.
112    pub fn to_atomics(&self, atomics: &[AtomicU64; Self::BITMAP_BLOCKS]) {
113        for (i, word) in self.0.iter().enumerate() {
114            atomics[i].store(*word, Ordering::Relaxed);
115        }
116    }
117}
118
119impl BitAnd for XpermsBitmap {
120    type Output = Self;
121    fn bitand(self, rhs: Self) -> Self::Output {
122        Self(std::array::from_fn(|i| self.0[i] & rhs.0[i]))
123    }
124}
125
126impl BitAndAssign for XpermsBitmap {
127    fn bitand_assign(&mut self, rhs: Self) {
128        for i in 0..4 {
129            self.0[i] &= rhs.0[i];
130        }
131    }
132}
133
134impl BitOr for XpermsBitmap {
135    type Output = Self;
136    fn bitor(self, rhs: Self) -> Self::Output {
137        Self(std::array::from_fn(|i| self.0[i] | rhs.0[i]))
138    }
139}
140
141impl BitOrAssign for XpermsBitmap {
142    fn bitor_assign(&mut self, rhs: Self) {
143        for i in 0..4 {
144            self.0[i] |= rhs.0[i];
145        }
146    }
147}
148
149impl Sub for XpermsBitmap {
150    type Output = Self;
151    fn sub(self, rhs: Self) -> Self {
152        Self(std::array::from_fn(|i| self.0[i] & !rhs.0[i]))
153    }
154}
155
156impl SubAssign for XpermsBitmap {
157    fn sub_assign(&mut self, rhs: Self) {
158        for i in 0..4 {
159            self.0[i] &= !rhs.0[i];
160        }
161    }
162}
163
164impl Not for XpermsBitmap {
165    type Output = Self;
166    fn not(self) -> Self::Output {
167        Self(self.0.map(|word| !word))
168    }
169}
170
171impl Validate for XpermsBitmap {
172    fn validate(&self, _policy: &Policy) -> Result<(), ValidateError> {
173        Ok(())
174    }
175}
176
177/// Extended permissions specification (e.g., ioctl commands or netlink message types) associated with an access vector rule.
178#[derive(Clone, Debug, PartialEq, Eq, Parse, Serialize)]
179pub struct ExtendedPermissions {
180    xperms_type: u8,
181    xperms_optional_prefix: u8,
182    xperms_bitmap: XpermsBitmap,
183}
184
185impl Validate for ExtendedPermissions {
186    fn validate(&self, _policy: &Policy) -> Result<(), ValidateError> {
187        match self.xperms_type {
188            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES
189            | XPERMS_TYPE_IOCTL_PREFIXES
190            | XPERMS_TYPE_NLMSG => Ok(()),
191            v => Err(ValidateError::InvalidExtendedPermissionsType { value: v }),
192        }
193    }
194}
195
196impl ExtendedPermissions {
197    /// Returns the raw extended permissions type identifier (e.g. ioctl or netlink message format).
198    pub fn xperms_type(&self) -> u8 {
199        self.xperms_type
200    }
201
202    /// Returns the optional 8-bit prefix specified by this extended permissions block, if any.
203    pub fn xperms_optional_prefix(&self) -> u8 {
204        self.xperms_optional_prefix
205    }
206
207    /// Returns a reference to the underlying [`XpermsBitmap`].
208    pub fn xperms_bitmap(&self) -> &XpermsBitmap {
209        &self.xperms_bitmap
210    }
211
212    /// Returns the total number of individual permissions specified by this bitmap.
213    #[cfg(test)]
214    pub fn count(&self) -> u64 {
215        let count = self
216            .xperms_bitmap
217            .0
218            .iter()
219            .fold(0, |count, block| count as u64 + block.count_ones() as u64);
220        match self.xperms_type {
221            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES | XPERMS_TYPE_NLMSG => count,
222            XPERMS_TYPE_IOCTL_PREFIXES => count * 0x100,
223            _ => unreachable!("invalid xperms_type in validated ExtendedPermissions"),
224        }
225    }
226
227    /// Returns `true` if the specified extended permission `xperm` is included in this rule.
228    #[cfg(test)]
229    pub fn contains(&self, xperm: u16) -> bool {
230        let [postfix, prefix] = xperm.to_le_bytes();
231        if (self.xperms_type == XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES
232            || self.xperms_type == XPERMS_TYPE_NLMSG)
233            && self.xperms_optional_prefix != prefix
234        {
235            return false;
236        }
237        let value = match self.xperms_type {
238            XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES | XPERMS_TYPE_NLMSG => postfix,
239            XPERMS_TYPE_IOCTL_PREFIXES => prefix,
240            _ => unreachable!("invalid xperms_type in validated ExtendedPermissions"),
241        };
242        self.xperms_bitmap.contains(value)
243    }
244}
245
246/// Compact enum identifying the type and target array for a rule in sequential policy order.
247#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
248pub enum RuleKind {
249    Allow,
250    AuditAllow,
251    DontAudit,
252    TypeTransition,
253    TypeMember,
254    TypeChange,
255    AllowXperm,
256    AuditAllowXperm,
257    DontAuditXperm,
258}
259
260impl TryFrom<u16> for RuleKind {
261    type Error = ParseError;
262
263    fn try_from(rule_type: u16) -> Result<Self, Self::Error> {
264        let base_kind = rule_type & !AV_ENABLED_RULE_FLAG;
265        match base_kind {
266            AV_ALLOW_RULE_FLAG => Ok(Self::Allow),
267            AV_AUDITALLOW_RULE_FLAG => Ok(Self::AuditAllow),
268            AV_DONTAUDIT_RULE_FLAG => Ok(Self::DontAudit),
269            AV_TYPE_TRANSITION_RULE_FLAG => Ok(Self::TypeTransition),
270            AV_TYPE_MEMBER_RULE_FLAG => Ok(Self::TypeMember),
271            AV_TYPE_CHANGE_RULE_FLAG => Ok(Self::TypeChange),
272            AV_ALLOWXPERM_RULE_FLAG => Ok(Self::AllowXperm),
273            AV_AUDITALLOWXPERM_RULE_FLAG => Ok(Self::AuditAllowXperm),
274            AV_DONTAUDITXPERM_RULE_FLAG => Ok(Self::DontAuditXperm),
275            _ => {
276                Err(ParseError::InvalidEnumValue { enum_name: "RuleKind", value: rule_type as u64 })
277            }
278        }
279    }
280}
281
282impl From<RuleKind> for u16 {
283    fn from(kind: RuleKind) -> Self {
284        match kind {
285            RuleKind::Allow => AV_ALLOW_RULE_FLAG,
286            RuleKind::AuditAllow => AV_AUDITALLOW_RULE_FLAG,
287            RuleKind::DontAudit => AV_DONTAUDIT_RULE_FLAG,
288            RuleKind::TypeTransition => AV_TYPE_TRANSITION_RULE_FLAG,
289            RuleKind::TypeMember => AV_TYPE_MEMBER_RULE_FLAG,
290            RuleKind::TypeChange => AV_TYPE_CHANGE_RULE_FLAG,
291            RuleKind::AllowXperm => AV_ALLOWXPERM_RULE_FLAG,
292            RuleKind::AuditAllowXperm => AV_AUDITALLOWXPERM_RULE_FLAG,
293            RuleKind::DontAuditXperm => AV_DONTAUDITXPERM_RULE_FLAG,
294        }
295    }
296}
297
298impl Validate for RuleKind {
299    fn validate(&self, _policy: &Policy) -> Result<(), ValidateError> {
300        Ok(())
301    }
302}
303
304/// Standard access vector rule (allow, auditallow, dontaudit).
305#[derive(Clone, Debug, Validate)]
306pub struct AccessRule {
307    key: RuleKey,
308    kind: RuleKind,
309    access_vector: AccessVector,
310    enabled: bool,
311}
312
313impl AccessRule {
314    /// Returns the [`AccessVector`] for this rule.
315    pub fn access_vector(&self) -> AccessVector {
316        self.access_vector
317    }
318}
319
320/// Type transition, change, or member rule.
321#[derive(Clone, Debug, Validate)]
322pub struct TypeRule {
323    key: RuleKey,
324    kind: RuleKind,
325    new_type: TypeId,
326    enabled: bool,
327}
328
329impl TypeRule {
330    /// Returns the target type ID for this rule transition.
331    pub fn new_type(&self) -> TypeId {
332        self.new_type
333    }
334}
335
336/// Extended permissions rule (allowxperm, auditallowxperm, dontauditxperm).
337#[derive(Clone, Debug, Validate)]
338pub struct XpermRule {
339    key: RuleKey,
340    kind: RuleKind,
341    extended_permissions: ExtendedPermissions,
342    enabled: bool,
343}
344
345impl XpermRule {
346    /// Returns the extended permissions block for this rule.
347    pub fn extended_permissions(&self) -> &ExtendedPermissions {
348        &self.extended_permissions
349    }
350}
351
352/// Lookup key for indexing and matching access vector rules by source domain, target domain, and class.
353#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Validate)]
354pub struct RuleKey {
355    source_type: TypeId,
356    target_type: TypeId,
357    class: ClassId,
358}
359
360impl RuleKey {
361    /// Constructs a [`RuleKey`] for the specified source domain, target domain, and security class.
362    fn new(source_type: TypeId, target_type: TypeId, class: ClassId) -> Self {
363        Self { source_type, target_type, class }
364    }
365
366    /// Hashes this [`RuleKey`].
367    fn hash(&self) -> u64 {
368        let packed = (self.source_type.as_u16() as u64)
369            | ((self.target_type.as_u16() as u64) << 16)
370            | ((self.class.as_u16() as u64) << 32);
371        rapidhash::rapidhash(&packed.to_ne_bytes())
372    }
373
374    /// Constructs a [`BinaryAccessVectorRuleHeader`] for this [`RuleKey`], [`RuleKind`], and enabled flag.
375    fn to_header(&self, kind: RuleKind, enabled: bool) -> BinaryAccessVectorRuleHeader {
376        let mut rule_flags = u16::from(kind);
377        if enabled {
378            rule_flags |= AV_ENABLED_RULE_FLAG;
379        }
380        BinaryAccessVectorRuleHeader {
381            source_type: self.source_type.as_u16(),
382            target_type: self.target_type.as_u16(),
383            class: self.class.as_u16(),
384            rule_flags,
385        }
386    }
387}
388
389/// Trait implemented by rule types that provide a [`RuleKey`] and [`RuleKind`].
390pub trait HasRuleKey {
391    /// Returns the [`RuleKey`] for this rule.
392    fn key(&self) -> RuleKey;
393
394    /// Returns the [`RuleKind`] for this rule.
395    fn kind(&self) -> RuleKind;
396}
397
398impl HasRuleKey for AccessRule {
399    fn key(&self) -> RuleKey {
400        self.key
401    }
402    fn kind(&self) -> RuleKind {
403        self.kind
404    }
405}
406
407impl HasRuleKey for TypeRule {
408    fn key(&self) -> RuleKey {
409        self.key
410    }
411    fn kind(&self) -> RuleKind {
412        self.kind
413    }
414}
415
416impl HasRuleKey for XpermRule {
417    fn key(&self) -> RuleKey {
418        self.key
419    }
420    fn kind(&self) -> RuleKind {
421        self.kind
422    }
423}
424
425/// Result of a permissions calculation between source & target domains for a specific class.
426///
427/// Decisions describe which permissions are allowed, and whether permissions should be
428/// audit-logged when allowed, and when denied.
429#[derive(Debug, Clone, PartialEq, Eq)]
430pub struct AccessDecision {
431    pub allow: AccessVector,
432    pub auditallow: AccessVector,
433    pub auditdeny: AccessVector,
434    pub flags: u32,
435
436    /// If this field is set then denials should be audit-logged with "todo_deny" as the reason, with
437    /// the `bug` number included in the audit message.
438    pub todo_bug: Option<NonZeroU32>,
439}
440
441impl Default for AccessDecision {
442    fn default() -> Self {
443        Self::allow(AccessVector::NONE)
444    }
445}
446
447impl AccessDecision {
448    /// Returns an [`AccessDecision`] with the specified permissions to `allow`, and default audit
449    /// behaviour.
450    pub const fn allow(allow: AccessVector) -> Self {
451        Self {
452            allow,
453            auditallow: AccessVector::NONE,
454            auditdeny: AccessVector::ALL,
455            flags: 0,
456            todo_bug: None,
457        }
458    }
459}
460
461/// Lookup table for SELinux rules, optimized for fast queries. It maps a [`RuleKey`]
462/// (source, target, class) to matching rules.
463///
464/// Rules are grouped into three contiguous arrays based on their payload struct:
465/// 1. [`AccessRule`] (`av_rules`): allow, auditallow, dontaudit.
466/// 2. [`TypeRule`] (`type_rules`): transitions.
467/// 3. [`XpermRule`] (`xperm_rules`): allowxperm, auditallowxperm, dontauditxperm.
468///
469/// Three corresponding [`HashTable`]s map [`RuleKey`]s to the index of the **first** matching rule.
470/// Lookups return an iterator that starts at that index and yields rules until the
471/// key changes. This works because binary policies guarantee rules for the same key
472/// are contiguous.
473#[derive(Debug, Clone)]
474struct AccessVectorRules {
475    av_rules: Box<[AccessRule]>,
476    type_rules: Box<[TypeRule]>,
477    xperm_rules: Box<[XpermRule]>,
478    rule_order: Box<[RuleKind]>,
479}
480
481impl AccessVectorRules {
482    /// Returns the standard access vector rules.
483    #[cfg(test)]
484    fn av_rules(&self) -> &[AccessRule] {
485        &self.av_rules
486    }
487}
488
489impl Parse for AccessVectorRules {
490    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
491        let count = u32::parse(cursor)? as usize;
492        let min_bytes = count.saturating_mul(std::mem::size_of::<BinaryAccessVectorRuleHeader>());
493        if min_bytes > cursor.remaining() {
494            return Err(ParseError::MissingData {
495                type_name: std::any::type_name::<BinaryAccessVectorRuleHeader>(),
496                type_size: min_bytes,
497                num_bytes: cursor.remaining(),
498            });
499        }
500
501        let mut av_rules = Vec::new();
502        let mut type_rules = Vec::new();
503        let mut xperm_rules = Vec::new();
504        let mut rule_order = Vec::with_capacity(count);
505
506        // Split rules out based on the three different kinds of payload (access vector, type, or
507        // extended permissions block).
508        for _ in 0..count {
509            let header = BinaryAccessVectorRuleHeader::parse(cursor)?;
510            let kind = RuleKind::try_from(header.rule_flags)?;
511            let enabled = (header.rule_flags & AV_ENABLED_RULE_FLAG) != 0;
512
513            let source_val = header.source_type;
514            let source_type = TypeId::from_u16(source_val)
515                .ok_or(ParseError::InvalidId { value: source_val as u32 })?;
516
517            let target_val = header.target_type;
518            let target_type = TypeId::from_u16(target_val)
519                .ok_or(ParseError::InvalidId { value: target_val as u32 })?;
520
521            let class_val = header.class;
522            let class = ClassId::from_u16(class_val)
523                .ok_or(ParseError::InvalidId { value: class_val as u32 })?;
524
525            let key = RuleKey::new(source_type, target_type, class);
526
527            match kind {
528                RuleKind::AllowXperm | RuleKind::AuditAllowXperm | RuleKind::DontAuditXperm => {
529                    let extended_permissions = ExtendedPermissions::parse(cursor)?;
530                    xperm_rules.push(XpermRule { key, kind, extended_permissions, enabled });
531                    rule_order.push(kind);
532                }
533                RuleKind::TypeTransition | RuleKind::TypeChange | RuleKind::TypeMember => {
534                    let new_type = TypeId::parse(cursor)?;
535                    type_rules.push(TypeRule { key, kind, new_type, enabled });
536                    rule_order.push(kind);
537                }
538                RuleKind::Allow | RuleKind::AuditAllow | RuleKind::DontAudit => {
539                    let access_vector = AccessVector::parse(cursor)?;
540                    av_rules.push(AccessRule { key, kind, access_vector, enabled });
541                    rule_order.push(kind);
542                }
543            }
544        }
545
546        let av_rules = av_rules.into_boxed_slice();
547        let type_rules = type_rules.into_boxed_slice();
548        let xperm_rules = xperm_rules.into_boxed_slice();
549        let rule_order = rule_order.into_boxed_slice();
550
551        Ok(Self { av_rules, type_rules, xperm_rules, rule_order })
552    }
553}
554
555impl Serialize for AccessVectorRules {
556    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
557        let count = self.rule_order.len() as u32;
558        count.serialize(writer)?;
559
560        let mut av_rules = self.av_rules.iter();
561        let mut type_rules = self.type_rules.iter();
562        let mut xperm_rules = self.xperm_rules.iter();
563
564        for &kind in self.rule_order.iter() {
565            match kind {
566                RuleKind::Allow | RuleKind::AuditAllow | RuleKind::DontAudit => {
567                    let rule = av_rules.next().unwrap();
568                    rule.key.to_header(kind, rule.enabled).serialize(writer)?;
569                    let val: u32 = rule.access_vector.into();
570                    val.serialize(writer)?;
571                }
572                RuleKind::TypeTransition | RuleKind::TypeChange | RuleKind::TypeMember => {
573                    let rule = type_rules.next().unwrap();
574                    rule.key.to_header(kind, rule.enabled).serialize(writer)?;
575                    rule.new_type.serialize(writer)?;
576                }
577                RuleKind::AllowXperm | RuleKind::AuditAllowXperm | RuleKind::DontAuditXperm => {
578                    let rule = xperm_rules.next().unwrap();
579                    rule.key.to_header(kind, rule.enabled).serialize(writer)?;
580                    rule.extended_permissions.serialize(writer)?;
581                }
582            }
583        }
584        Ok(())
585    }
586}
587
588impl Validate for AccessVectorRules {
589    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
590        self.av_rules.validate(policy)?;
591        self.type_rules.validate(policy)?;
592        self.xperm_rules.validate(policy)?;
593        Ok(())
594    }
595}
596
597/// Global access vector rules wrapper that indexes rules by source, target, and class.
598///
599/// Binary policies guarantee that rules for the same key in the global table are contiguous.
600/// Three corresponding [`HashTable`]s map [`RuleKey`]s to the index of the **first** matching rule.
601/// Lookups return an iterator that starts at that index and yields rules until the key changes.
602#[derive(Debug)]
603pub struct IndexedAccessVectorRules {
604    rules: AccessVectorRules,
605    av_table: HashTable<U24Index>,
606    type_transition_table: HashTable<U24Index>,
607    xperms_table: HashTable<U24Index>,
608}
609
610impl IndexedAccessVectorRules {
611    /// Builds an index over the specified access vector rules.
612    ///
613    /// Returns [`ParseError::DuplicateAccessVectorRule`] if non-contiguous rules share the same key.
614    fn new(rules: AccessVectorRules) -> Result<Self, ParseError> {
615        let av_table = build_index(&rules.av_rules)?;
616        let type_transition_table = build_index(&rules.type_rules)?;
617        let xperms_table = build_index(&rules.xperm_rules)?;
618
619        Ok(Self { rules, av_table, type_transition_table, xperms_table })
620    }
621
622    fn find_rules<'a, R: HasRuleKey>(
623        table: &HashTable<U24Index>,
624        rules: &'a [R],
625        key: RuleKey,
626    ) -> impl Iterator<Item = &'a R> {
627        let hash = key.hash();
628        let slice = match table.find(hash, |&i| rules[usize::from(i)].key() == key) {
629            Some(&i) => &rules[usize::from(i)..],
630            None => &[],
631        };
632        slice.iter().take_while(move |rule| rule.key() == key)
633    }
634
635    /// Returns an iterator yielding matching standard access vector rules for the specified tuple.
636    pub fn find_av_rules(
637        &self,
638        source: TypeId,
639        target: TypeId,
640        class: ClassId,
641    ) -> impl Iterator<Item = &AccessRule> {
642        Self::find_rules(&self.av_table, &self.rules.av_rules, RuleKey::new(source, target, class))
643    }
644
645    /// Returns an iterator yielding matching type transition, change, or member rules for the specified tuple.
646    pub fn find_type_rules(
647        &self,
648        source: TypeId,
649        target: TypeId,
650        class: ClassId,
651    ) -> impl Iterator<Item = &TypeRule> {
652        Self::find_rules(
653            &self.type_transition_table,
654            &self.rules.type_rules,
655            RuleKey::new(source, target, class),
656        )
657    }
658
659    /// Returns an iterator yielding matching extended permission rules for the specified tuple.
660    pub fn find_xperm_rules(
661        &self,
662        source: TypeId,
663        target: TypeId,
664        class: ClassId,
665    ) -> impl Iterator<Item = &XpermRule> {
666        Self::find_rules(
667            &self.xperms_table,
668            &self.rules.xperm_rules,
669            RuleKey::new(source, target, class),
670        )
671    }
672}
673
674impl Parse for IndexedAccessVectorRules {
675    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
676        let rules = AccessVectorRules::parse(cursor)?;
677        Self::new(rules)
678    }
679}
680
681impl Serialize for IndexedAccessVectorRules {
682    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
683        self.rules.serialize(writer)
684    }
685}
686
687impl Validate for IndexedAccessVectorRules {
688    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
689        self.rules.validate(policy)
690    }
691}
692
693/// Builds a [`HashTable<U24Index>`] mapping [`RuleKey`] hashes to the index of the first rule in each contiguous run.
694///
695/// Binary SELinux policies index rules into buckets by `(source, target, class)`, within which rules are sorted by
696/// `(source, target, class, rule_kind)`. Therefore, all rules sharing a given [`RuleKey`] are guaranteed to be contiguous
697/// and well-ordered.
698///
699/// Returns [`ParseError::DuplicateAccessVectorRule`] if non-contiguous rules share the same key.
700fn build_index<R: HasRuleKey>(rules: &[R]) -> Result<HashTable<U24Index>, ParseError> {
701    let mut table = HashTable::new();
702    let mut offset = 0;
703
704    for chunk in rules.chunk_by(|a, b| a.key() == b.key()) {
705        let key = chunk[0].key();
706        let u24_idx = offset.try_into()?;
707        let hash = key.hash();
708
709        let Entry::Vacant(entry) = table.entry(
710            hash,
711            |&i| rules[usize::from(i)].key() == key,
712            |&i| rules[usize::from(i)].key().hash(),
713        ) else {
714            return Err(ParseError::DuplicateAccessVectorRule { key, kind: chunk[0].kind() });
715        };
716        entry.insert(u24_idx);
717        offset += chunk.len();
718    }
719
720    Ok(table)
721}
722
723/// Expression element kind bit for boolean variable operands.
724const COND_EXPR_BOOL: u32 = 1;
725/// Expression element kind bit for unary NOT operator.
726const COND_EXPR_NOT: u32 = 2;
727/// Expression element kind bit for binary OR operator.
728const COND_EXPR_OR: u32 = 3;
729/// Expression element kind bit for binary AND operator.
730const COND_EXPR_AND: u32 = 4;
731/// Expression element kind bit for binary EQUALS operator.
732const COND_EXPR_EQ: u32 = 5;
733/// Expression element kind bit for binary NOT-EQUALS operator.
734const COND_EXPR_NEQ: u32 = 6;
735
736/// Individual element in a conditional boolean expression sequence.
737#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
738enum ConditionalExpressionElement {
739    /// Conditional boolean variable operand.
740    Boolean(ConditionalBooleanId),
741    /// Unary boolean NOT operator.
742    Not,
743    /// Binary boolean OR operator.
744    Or,
745    /// Binary boolean AND operator.
746    And,
747    /// Binary boolean EQUALS operator.
748    Equals,
749    /// Binary boolean NOT-EQUALS operator.
750    NotEquals,
751}
752
753impl Parse for ConditionalExpressionElement {
754    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
755        let expr_type = u32::parse(cursor)?;
756        let boolean_id = u32::parse(cursor)?;
757        match expr_type {
758            COND_EXPR_BOOL => {
759                let id = ConditionalBooleanId::from_u32(boolean_id)
760                    .ok_or(ParseError::InvalidId { value: boolean_id })?;
761                Ok(Self::Boolean(id))
762            }
763            COND_EXPR_NOT => Ok(Self::Not),
764            COND_EXPR_OR => Ok(Self::Or),
765            COND_EXPR_AND => Ok(Self::And),
766            COND_EXPR_EQ => Ok(Self::Equals),
767            COND_EXPR_NEQ => Ok(Self::NotEquals),
768            invalid => Err(ParseError::InvalidEnumValue {
769                enum_name: "ConditionalExpressionElement",
770                value: invalid as u64,
771            }),
772        }
773    }
774}
775
776impl Serialize for ConditionalExpressionElement {
777    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
778        let (expr_type, boolean_id) = match self {
779            Self::Boolean(id) => (COND_EXPR_BOOL, id.as_u32()),
780            Self::Not => (COND_EXPR_NOT, 0),
781            Self::Or => (COND_EXPR_OR, 0),
782            Self::And => (COND_EXPR_AND, 0),
783            Self::Equals => (COND_EXPR_EQ, 0),
784            Self::NotEquals => (COND_EXPR_NEQ, 0),
785        };
786        expr_type.serialize(writer)?;
787        boolean_id.serialize(writer)?;
788        Ok(())
789    }
790}
791
792impl Validate for ConditionalExpressionElement {
793    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
794        if let Self::Boolean(id) = self {
795            id.validate(policy)?;
796        }
797        Ok(())
798    }
799}
800
801/// Parsed SELinux conditional node containing expression AST and true/false branch rule sets.
802#[derive(Clone, Debug, Parse, Serialize, Validate)]
803pub struct ConditionalNode {
804    state: u32,
805    expression_elements: Array<ConditionalExpressionElement>,
806    true_rules: AccessVectorRules,
807    false_rules: AccessVectorRules,
808}
809
810/// On-wire header identifying the source, target, class, and rule flags of an access vector rule.
811#[derive(Clone, Debug, Eq, PartialEq, Hash, Parse, Serialize)]
812struct BinaryAccessVectorRuleHeader {
813    source_type: u16,
814    target_type: u16,
815    class: u16,
816    rule_flags: u16,
817}
818
819#[cfg(test)]
820mod tests {
821    use super::*;
822    use crate::policy::metadata::PolicyVersion;
823    use crate::policy::parser::PolicyWriter;
824    use crate::policy::traits::{HasPolicyId, PolicyId};
825
826    #[test]
827    fn test_xperms_bitmap_ops_and_contains() {
828        let mut bitmap1 = XpermsBitmap::NONE;
829        let mut bitmap2 = XpermsBitmap::NONE;
830        assert!(!bitmap1.contains(10));
831        assert!(!bitmap2.contains(10));
832
833        bitmap1.0[0] = 1 << 10;
834        bitmap2.0[0] = 1 << 20;
835        assert!(bitmap1.contains(10));
836        assert!(!bitmap1.contains(20));
837        assert!(bitmap2.contains(20));
838
839        let or_bitmap = bitmap1 | bitmap2;
840        assert!(or_bitmap.contains(10));
841        assert!(or_bitmap.contains(20));
842
843        let and_bitmap = or_bitmap & bitmap1;
844        assert!(and_bitmap.contains(10));
845        assert!(!and_bitmap.contains(20));
846
847        let sub_bitmap = or_bitmap - bitmap1;
848        assert!(!sub_bitmap.contains(10));
849        assert!(sub_bitmap.contains(20));
850
851        let not_bitmap = !XpermsBitmap::NONE;
852        assert_eq!(not_bitmap, XpermsBitmap::ALL);
853        assert!(not_bitmap.contains(0));
854        assert!(not_bitmap.contains(255));
855    }
856
857    #[test]
858    fn test_av_rule_parse_and_serialize() {
859        let data = [
860            1, 0, 0, 0, // count = 1
861            1, 0, // source_type = 1
862            2, 0, // target_type = 2
863            3, 0, // class = 3
864            1, 0, // rule_type = 1 (ALLOW)
865            5, 0, 0, 0, // access_vector = 5
866        ];
867        let mut cursor = PolicyCursor::new(&data);
868        let av_rules = AccessVectorRules::parse(&mut cursor).expect("parse rules table");
869        assert_eq!(av_rules.av_rules.len(), 1);
870        assert_eq!(av_rules.av_rules[0].key.source_type, TypeId::from_u32(1).unwrap());
871        assert_eq!(av_rules.av_rules[0].key.target_type, TypeId::from_u32(2).unwrap());
872        assert_eq!(av_rules.av_rules[0].key.class, ClassId::from_u32(3).unwrap());
873        assert_eq!(av_rules.av_rules[0].access_vector, AccessVector::from(5));
874
875        let mut writer = Vec::new();
876        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
877        av_rules.serialize(&mut policy_writer).expect("serialize rules table");
878        assert_eq!(writer.as_slice(), &data);
879    }
880
881    #[test]
882    fn test_av_rule_type_transition_parse_and_serialize() {
883        let data = [
884            1, 0, 0, 0, // count = 1
885            1, 0, // source_type = 1
886            2, 0, // target_type = 2
887            3, 0, // class = 3
888            16, 0, // rule_type = 16 (TYPE_TRANSITION)
889            10, 0, 0, 0, // new_type = 10
890        ];
891        let mut cursor = PolicyCursor::new(&data);
892        let av_rules = AccessVectorRules::parse(&mut cursor).expect("parse rules table");
893        assert_eq!(av_rules.type_rules.len(), 1);
894        assert_eq!(av_rules.type_rules[0].key.source_type, TypeId::from_u32(1).unwrap());
895        assert_eq!(av_rules.type_rules[0].key.target_type, TypeId::from_u32(2).unwrap());
896        assert_eq!(av_rules.type_rules[0].key.class, ClassId::from_u32(3).unwrap());
897        assert_eq!(av_rules.type_rules[0].new_type, TypeId::from_u32(10).unwrap());
898
899        let mut writer = Vec::new();
900        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
901        av_rules.serialize(&mut policy_writer).expect("serialize rules table");
902        assert_eq!(writer.as_slice(), &data);
903    }
904
905    #[test]
906    fn test_av_rule_xperm_parse_and_serialize() {
907        let mut data = vec![
908            1, 0, 0, 0, // count = 1
909            1, 0, // source_type = 1
910            2, 0, // target_type = 2
911            3, 0, // class = 3
912            0, 1, // rule_type = 0x0100 (ALLOWXPERM)
913            1, // xperms_type = 1 (XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES)
914            0, // xperms_optional_prefix = 0
915        ];
916        data.extend_from_slice(&[
917            1, 0, 0, 0, 0, 0, 0, 0, // word 0 = 1
918            0, 0, 0, 0, 0, 0, 0, 0, // word 1 = 0
919            0, 0, 0, 0, 0, 0, 0, 0, // word 2 = 0
920            0, 0, 0, 0, 0, 0, 0, 0, // word 3 = 0
921        ]);
922        let mut cursor = PolicyCursor::new(&data);
923        let av_rules = AccessVectorRules::parse(&mut cursor).expect("parse rules table");
924        assert_eq!(av_rules.xperm_rules.len(), 1);
925        let xp = &av_rules.xperm_rules[0].extended_permissions;
926        assert_eq!(xp.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
927        assert!(xp.xperms_bitmap.contains(0));
928        assert!(!xp.xperms_bitmap.contains(1));
929
930        let mut writer = Vec::new();
931        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
932        av_rules.serialize(&mut policy_writer).expect("serialize rules table");
933        assert_eq!(writer.as_slice(), &data);
934    }
935
936    #[test]
937    fn test_access_vector_rules_indexing_and_decisions() {
938        let data = [
939            2, 0, 0, 0, // count = 2 rules
940            // Rule 1: ALLOW (source 1, target 2, class 3)
941            1, 0, // source = 1
942            2, 0, // target = 2
943            3, 0, // class = 3
944            1, 0, // rule_type = 1 (ALLOW)
945            7, 0, 0, 0, // access_vector = 7
946            // Rule 2: TYPE_TRANSITION (source 1, target 2, class 3 -> new_type 9)
947            1, 0, // source = 1
948            2, 0, // target = 2
949            3, 0, // class = 3
950            16, 0, // rule_type = 16 (TYPE_TRANSITION)
951            9, 0, 0, 0, // new_type = 9
952        ];
953
954        let mut cursor = PolicyCursor::new(&data);
955        let av_rules = IndexedAccessVectorRules::parse(&mut cursor).expect("parse rules table");
956
957        let s1 = TypeId::from_u32(1).unwrap();
958        let t2 = TypeId::from_u32(2).unwrap();
959        let c3 = ClassId::from_u32(3).unwrap();
960
961        let av_rules_list: Vec<_> = av_rules.find_av_rules(s1, t2, c3).collect();
962        assert_eq!(av_rules_list.len(), 1);
963        assert_eq!(av_rules_list[0].kind(), RuleKind::Allow);
964        assert_eq!(av_rules_list[0].access_vector(), AccessVector::from(7));
965
966        let type_rules_list: Vec<_> = av_rules.find_type_rules(s1, t2, c3).collect();
967        assert_eq!(type_rules_list.len(), 1);
968        assert_eq!(type_rules_list[0].kind(), RuleKind::TypeTransition);
969        assert_eq!(type_rules_list[0].new_type(), TypeId::from_u32(9).unwrap());
970    }
971
972    #[test]
973    fn test_unindexed_access_vector_rules_allows_duplicate_keys() {
974        let data = [
975            3, 0, 0, 0, // count = 3 rules
976            // Rule 1: ALLOW (source 1, target 2, class 3)
977            1, 0, 2, 0, 3, 0, 1, 0, 7, 0, 0, 0, // access_vector = 7
978            // Rule 2: ALLOW (source 4, target 5, class 6)
979            4, 0, 5, 0, 6, 0, 1, 0, 8, 0, 0, 0, // access_vector = 8
980            // Rule 3: ALLOW (source 1, target 2, class 3) -- non-consecutive duplicate key
981            1, 0, 2, 0, 3, 0, 1, 0, 9, 0, 0, 0, // access_vector = 9
982        ];
983
984        let mut cursor = PolicyCursor::new(&data);
985        let av_rules =
986            AccessVectorRules::parse(&mut cursor).expect("unindexed rules parse duplicate keys");
987        assert_eq!(av_rules.av_rules.len(), 3);
988
989        let mut cursor = PolicyCursor::new(&data);
990        let err = IndexedAccessVectorRules::parse(&mut cursor)
991            .expect_err("indexed rules reject duplicate keys");
992        assert!(matches!(
993            err,
994            ParseError::DuplicateAccessVectorRule { key: _, kind: RuleKind::Allow }
995        ));
996    }
997
998    #[test]
999    fn test_conditional_nodes() {
1000        let policy_bytes =
1001            include_bytes!("../../testdata/composite_policies/compiled/conditional_policy");
1002        let policy = Policy::parse_and_validate(policy_bytes);
1003
1004        let nodes = &policy.conditional_nodes;
1005        assert_eq!(nodes.len(), 4);
1006        assert_eq!(nodes[0].true_rules.av_rules().len(), 1);
1007        assert_eq!(nodes[0].false_rules.av_rules().len(), 0);
1008        assert_eq!(nodes[1].true_rules.av_rules().len(), 1);
1009        assert_eq!(nodes[1].false_rules.av_rules().len(), 1);
1010        assert_eq!(nodes[2].true_rules.av_rules().len(), 2);
1011        assert_eq!(nodes[2].false_rules.av_rules().len(), 0);
1012        assert_eq!(nodes[3].true_rules.av_rules().len(), 1);
1013        assert_eq!(nodes[3].false_rules.av_rules().len(), 0);
1014    }
1015
1016    #[test]
1017    fn parse_allowxperm_one_ioctl() {
1018        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1019        let policy = Policy::parse_and_validate(policy_bytes);
1020
1021        let class_id =
1022            policy.classes().get_by_name(b"class_one_ioctl").expect("look up class_one_ioctl").id();
1023        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1024        let rules: Vec<_> = policy
1025            .access_vector_rules
1026            .find_xperm_rules(type0, type0, class_id)
1027            .filter(|r| r.kind() == RuleKind::AllowXperm)
1028            .map(|r| r.extended_permissions())
1029            .collect();
1030
1031        assert_eq!(rules.len(), 1);
1032        assert_eq!(rules[0].count(), 1);
1033        assert!(rules[0].contains(0xabcd));
1034    }
1035
1036    // `ioctl` extended permissions that are declared in the same rule, and have the same
1037    // high byte, are stored in the same `AccessVectorRule` in the compiled policy.
1038    #[test]
1039    fn parse_allowxperm_two_ioctls_same_range_coalesced() {
1040        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1041        let policy = Policy::parse_and_validate(policy_bytes);
1042
1043        let class_id = policy
1044            .classes()
1045            .get_by_name(b"class_two_ioctls_same_range")
1046            .expect("look up class_two_ioctls_same_range")
1047            .id();
1048        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1049        let rules: Vec<_> = policy
1050            .access_vector_rules
1051            .find_xperm_rules(type0, type0, class_id)
1052            .filter(|r| r.kind() == RuleKind::AllowXperm)
1053            .map(|r| r.extended_permissions())
1054            .collect();
1055
1056        assert_eq!(rules.len(), 1);
1057        assert_eq!(rules[0].xperms_type(), XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1058        assert_eq!(rules[0].xperms_optional_prefix(), 0x12);
1059        assert_eq!(rules[0].count(), 2);
1060        assert!(rules[0].contains(0x1234));
1061        assert!(rules[0].contains(0x1256));
1062    }
1063
1064    #[test]
1065    fn parse_allowxperm_one_driver_range() {
1066        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1067        let policy = Policy::parse_and_validate(policy_bytes);
1068
1069        let class_id = policy
1070            .classes()
1071            .get_by_name(b"class_one_driver_range")
1072            .expect("look up class_one_driver_range")
1073            .id();
1074        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1075        let rules: Vec<_> = policy
1076            .access_vector_rules
1077            .find_xperm_rules(type0, type0, class_id)
1078            .filter(|r| r.kind() == RuleKind::AllowXperm)
1079            .map(|r| r.extended_permissions())
1080            .collect();
1081
1082        assert_eq!(rules.len(), 1);
1083        assert_eq!(rules[0].xperms_type(), XPERMS_TYPE_IOCTL_PREFIXES);
1084        assert_eq!(rules[0].count(), 0x100);
1085        assert!(rules[0].contains(0x1000));
1086        assert!(rules[0].contains(0x10ab));
1087    }
1088
1089    #[test]
1090    fn parse_allowxperm_two_ioctls_different_range() {
1091        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1092        let policy = Policy::parse_and_validate(policy_bytes);
1093
1094        let class_id = policy
1095            .classes()
1096            .get_by_name(b"class_two_ioctls_diff_range")
1097            .expect("look up class_two_ioctls_diff_range")
1098            .id();
1099        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1100        let rules: Vec<_> = policy
1101            .access_vector_rules
1102            .find_xperm_rules(type0, type0, class_id)
1103            .filter(|r| r.kind() == RuleKind::AllowXperm)
1104            .map(|r| r.extended_permissions())
1105            .collect();
1106
1107        assert_eq!(rules.len(), 2);
1108        assert_eq!(rules[0].xperms_type(), XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1109        assert_eq!(rules[0].xperms_optional_prefix(), 0x56);
1110        assert_eq!(rules[0].count(), 1);
1111        assert!(rules[0].contains(0x5678));
1112        assert_eq!(rules[1].xperms_type(), XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1113        assert_eq!(rules[1].xperms_optional_prefix(), 0x12);
1114        assert_eq!(rules[1].count(), 1);
1115        assert!(rules[1].contains(0x1234));
1116    }
1117
1118    #[test]
1119    fn parse_allowxperm_one_nlmsg() {
1120        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1121        let policy = Policy::parse_and_validate(policy_bytes);
1122
1123        let class_id =
1124            policy.classes().get_by_name(b"class_one_nlmsg").expect("look up class_one_nlmsg").id();
1125        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1126        let rules: Vec<_> = policy
1127            .access_vector_rules
1128            .find_xperm_rules(type0, type0, class_id)
1129            .filter(|r| r.kind() == RuleKind::AllowXperm)
1130            .map(|r| r.extended_permissions())
1131            .collect();
1132
1133        assert_eq!(rules.len(), 1);
1134        assert_eq!(rules[0].xperms_type(), XPERMS_TYPE_NLMSG);
1135        assert_eq!(rules[0].count(), 1);
1136        assert!(rules[0].contains(0x12));
1137    }
1138
1139    // If an allowxperm rule and an auditallowxperm rule specify exactly the same permissions, they
1140    // are not coalesced into a single `AccessVectorRule` in the policy; two rules appear in the
1141    // policy.
1142    #[test]
1143    fn parse_auditallowxperm_not_coalesced() {
1144        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1145        let policy = Policy::parse_and_validate(policy_bytes);
1146
1147        let class_id = policy
1148            .classes()
1149            .get_by_name(b"class_auditallowxperm_not_coalesced")
1150            .expect("look up class_auditallowxperm_not_coalesced")
1151            .id();
1152        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1153        let allow_rules: Vec<_> = policy
1154            .access_vector_rules
1155            .find_xperm_rules(type0, type0, class_id)
1156            .filter(|r| r.kind() == RuleKind::AllowXperm)
1157            .map(|r| r.extended_permissions())
1158            .collect();
1159        let auditallow_rules: Vec<_> = policy
1160            .access_vector_rules
1161            .find_xperm_rules(type0, type0, class_id)
1162            .filter(|r| r.kind() == RuleKind::AuditAllowXperm)
1163            .map(|r| r.extended_permissions())
1164            .collect();
1165
1166        assert_eq!(allow_rules.len(), 1);
1167        assert_eq!(allow_rules[0].count(), 1);
1168        assert!(allow_rules[0].contains(0xabcd));
1169        assert_eq!(auditallow_rules.len(), 1);
1170        assert_eq!(auditallow_rules[0].count(), 1);
1171        assert!(auditallow_rules[0].contains(0xabcd));
1172    }
1173
1174    #[test]
1175    fn parse_dontauditxperm() {
1176        let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy");
1177        let policy = Policy::parse_and_validate(policy_bytes);
1178
1179        let class_id = policy
1180            .classes()
1181            .get_by_name(b"class_dontauditxperm")
1182            .expect("look up class_dontauditxperm")
1183            .id();
1184        let type0 = policy.types().get_by_name(b"type0").expect("look up type0").id();
1185        let rules: Vec<_> = policy
1186            .access_vector_rules
1187            .find_xperm_rules(type0, type0, class_id)
1188            .filter(|r| r.kind() == RuleKind::DontAuditXperm)
1189            .map(|r| r.extended_permissions())
1190            .collect();
1191
1192        assert_eq!(rules.len(), 2);
1193        assert_eq!(rules[0].xperms_type(), XPERMS_TYPE_NLMSG);
1194        assert_eq!(rules[0].xperms_optional_prefix(), 0x00);
1195        assert_eq!(rules[0].count(), 1);
1196        assert!(rules[0].contains(0x11));
1197        assert_eq!(rules[1].xperms_type(), XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1198        assert_eq!(rules[1].xperms_optional_prefix(), 0x10);
1199        assert_eq!(rules[1].count(), 1);
1200        assert!(rules[1].contains(0x1000));
1201    }
1202}