Skip to main content

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