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