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::sync::atomic::{AtomicU64, Ordering};
7
8use hashbrown::HashTable;
9use hashbrown::hash_table::Entry;
10use rapidhash::RapidBuildHasher;
11
12use super::error::{ParseError, SerializeError, ValidateError};
13use super::parser::{Array, PolicyCursor};
14use super::traits::{Parse, PolicyId, Serialize, Validate};
15use super::{AccessVector, ClassId, ConditionalBooleanId, NewPolicy, TypeId, U24Index};
16
17pub use selinux_policy_derive::{Parse, Serialize};
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 Vec<u8>) -> 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 std::ops::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 std::ops::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 std::ops::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 std::ops::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 std::ops::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 std::ops::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 std::ops::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
297/// Standard access vector rule (allow, auditallow, dontaudit).
298#[derive(Clone, Debug, PartialEq, Eq)]
299pub struct AccessRule {
300    key: RuleKey,
301    kind: RuleKind,
302    access_vector: AccessVector,
303    enabled: bool,
304}
305
306impl AccessRule {
307    /// Returns the [`AccessVector`] for this rule.
308    pub fn access_vector(&self) -> AccessVector {
309        self.access_vector
310    }
311
312    /// Returns whether this rule is enabled.
313    pub fn enabled(&self) -> bool {
314        self.enabled
315    }
316}
317
318/// Type transition, change, or member rule.
319#[derive(Clone, Debug, PartialEq, Eq)]
320pub struct TypeRule {
321    key: RuleKey,
322    kind: RuleKind,
323    new_type: TypeId,
324    enabled: bool,
325}
326
327impl TypeRule {
328    /// Returns the target type ID for this rule transition.
329    pub fn new_type(&self) -> TypeId {
330        self.new_type
331    }
332
333    /// Returns whether this rule is enabled.
334    pub fn enabled(&self) -> bool {
335        self.enabled
336    }
337}
338
339/// Extended permissions rule (allowxperm, auditallowxperm, dontauditxperm).
340#[derive(Clone, Debug, PartialEq, Eq)]
341pub struct XpermRule {
342    key: RuleKey,
343    kind: RuleKind,
344    extended_permissions: ExtendedPermissions,
345    enabled: bool,
346}
347
348impl XpermRule {
349    /// Returns the extended permissions block for this rule.
350    pub fn extended_permissions(&self) -> &ExtendedPermissions {
351        &self.extended_permissions
352    }
353
354    /// Returns whether this rule is enabled.
355    pub fn enabled(&self) -> bool {
356        self.enabled
357    }
358}
359
360/// Lookup key for indexing and matching access vector rules by source domain, target domain, and class.
361#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
362pub struct RuleKey {
363    source_type: TypeId,
364    target_type: TypeId,
365    class: ClassId,
366}
367
368impl RuleKey {
369    /// Constructs a [`RuleKey`] for the specified source domain, target domain, and security class.
370    pub fn new(source_type: TypeId, target_type: TypeId, class: ClassId) -> Self {
371        Self { source_type, target_type, class }
372    }
373
374    /// Hashes this [`RuleKey`] using `hasher`.
375    pub fn hash(&self, hasher: &RapidBuildHasher) -> u64 {
376        use std::hash::{BuildHasher, Hash, Hasher};
377        let mut state = hasher.build_hasher();
378        Hash::hash(self, &mut state);
379        state.finish()
380    }
381
382    /// Constructs a [`BinaryAccessVectorRuleHeader`] for this [`RuleKey`], [`RuleKind`], and enabled flag.
383    fn to_header(&self, kind: RuleKind, enabled: bool) -> BinaryAccessVectorRuleHeader {
384        let mut rule_flags = u16::from(kind);
385        if enabled {
386            rule_flags |= AV_ENABLED_RULE_FLAG;
387        }
388        BinaryAccessVectorRuleHeader {
389            source_type: self.source_type.as_u16(),
390            target_type: self.target_type.as_u16(),
391            class: self.class.as_u16(),
392            rule_flags,
393        }
394    }
395}
396
397impl Validate for RuleKey {
398    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
399        self.source_type.validate(policy)?;
400        self.target_type.validate(policy)?;
401        self.class.validate(policy)?;
402        Ok(())
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, PartialEq, Eq)]
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    pub fn av_rules(&self) -> &[AccessRule] {
501        &self.av_rules
502    }
503
504    /// Returns the type transition, change, and member rules.
505    pub fn type_rules(&self) -> &[TypeRule] {
506        &self.type_rules
507    }
508
509    /// Returns the extended permission rules.
510    pub fn xperm_rules(&self) -> &[XpermRule] {
511        &self.xperm_rules
512    }
513
514    /// Returns the order of rule kinds as parsed from the policy.
515    pub fn rule_order(&self) -> &[RuleKind] {
516        &self.rule_order
517    }
518}
519
520impl Parse for AccessVectorRules {
521    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
522        let count = u32::parse(cursor)? as usize;
523
524        let mut av_rules = Vec::new();
525        let mut type_rules = Vec::new();
526        let mut xperm_rules = Vec::new();
527        let mut rule_order = Vec::with_capacity(count);
528
529        // Split rules out based on the three different kinds of payload (access vector, type, or
530        // extended permissions block).
531        for _ in 0..count {
532            let header = BinaryAccessVectorRuleHeader::parse(cursor)?;
533            let kind = RuleKind::try_from(header.rule_flags)?;
534            let enabled = (header.rule_flags & AV_ENABLED_RULE_FLAG) != 0;
535
536            let source_val = header.source_type;
537            let source_type = TypeId::from_u16(source_val)
538                .ok_or(ParseError::InvalidId { value: source_val as u32 })?;
539
540            let target_val = header.target_type;
541            let target_type = TypeId::from_u16(target_val)
542                .ok_or(ParseError::InvalidId { value: target_val as u32 })?;
543
544            let class_val = header.class;
545            let class = ClassId::from_u16(class_val)
546                .ok_or(ParseError::InvalidId { value: class_val as u32 })?;
547
548            let key = RuleKey::new(source_type, target_type, class);
549
550            match kind {
551                RuleKind::AllowXperm | RuleKind::AuditAllowXperm | RuleKind::DontAuditXperm => {
552                    let extended_permissions = ExtendedPermissions::parse(cursor)?;
553                    xperm_rules.push(XpermRule { key, kind, extended_permissions, enabled });
554                    rule_order.push(kind);
555                }
556                RuleKind::TypeTransition | RuleKind::TypeChange | RuleKind::TypeMember => {
557                    let new_type = TypeId::parse(cursor)?;
558                    type_rules.push(TypeRule { key, kind, new_type, enabled });
559                    rule_order.push(kind);
560                }
561                RuleKind::Allow | RuleKind::AuditAllow | RuleKind::DontAudit => {
562                    let access_vector = AccessVector::parse(cursor)?;
563                    av_rules.push(AccessRule { key, kind, access_vector, enabled });
564                    rule_order.push(kind);
565                }
566            }
567        }
568
569        let av_rules = av_rules.into_boxed_slice();
570        let type_rules = type_rules.into_boxed_slice();
571        let xperm_rules = xperm_rules.into_boxed_slice();
572        let rule_order = rule_order.into_boxed_slice();
573
574        Ok(Self { av_rules, type_rules, xperm_rules, rule_order })
575    }
576}
577
578impl Serialize for AccessVectorRules {
579    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
580        let count = self.rule_order.len() as u32;
581        count.serialize(writer)?;
582
583        let mut av_rules = self.av_rules.iter();
584        let mut type_rules = self.type_rules.iter();
585        let mut xperm_rules = self.xperm_rules.iter();
586
587        for &kind in self.rule_order.iter() {
588            match kind {
589                RuleKind::Allow | RuleKind::AuditAllow | RuleKind::DontAudit => {
590                    let rule = av_rules.next().unwrap();
591                    rule.key.to_header(kind, rule.enabled).serialize(writer)?;
592                    let val: u32 = rule.access_vector.into();
593                    val.serialize(writer)?;
594                }
595                RuleKind::TypeTransition | RuleKind::TypeChange | RuleKind::TypeMember => {
596                    let rule = type_rules.next().unwrap();
597                    rule.key.to_header(kind, rule.enabled).serialize(writer)?;
598                    rule.new_type.serialize(writer)?;
599                }
600                RuleKind::AllowXperm | RuleKind::AuditAllowXperm | RuleKind::DontAuditXperm => {
601                    let rule = xperm_rules.next().unwrap();
602                    rule.key.to_header(kind, rule.enabled).serialize(writer)?;
603                    rule.extended_permissions.serialize(writer)?;
604                }
605            }
606        }
607        Ok(())
608    }
609}
610
611impl Validate for AccessVectorRules {
612    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
613        for rule in self.av_rules.iter() {
614            rule.key.validate(policy)?;
615        }
616        for rule in self.type_rules.iter() {
617            rule.key.validate(policy)?;
618            rule.new_type.validate(policy)?;
619        }
620        for rule in self.xperm_rules.iter() {
621            rule.key.validate(policy)?;
622            rule.extended_permissions.validate(policy)?;
623        }
624        Ok(())
625    }
626}
627
628/// Global access vector rules wrapper that indexes rules by source, target, and class.
629///
630/// Binary policies guarantee that rules for the same key in the global table are contiguous.
631/// Three corresponding [`HashTable`]s map [`RuleKey`]s to the index of the **first** matching rule.
632/// Lookups return an iterator that starts at that index and yields rules until the key changes.
633#[derive(Debug, Clone)]
634pub struct IndexedAccessVectorRules {
635    rules: AccessVectorRules,
636    av_table: HashTable<U24Index>,
637    type_transition_table: HashTable<U24Index>,
638    xperms_table: HashTable<U24Index>,
639    hasher: RapidBuildHasher,
640}
641
642impl IndexedAccessVectorRules {
643    /// Builds an index over the specified access vector rules.
644    ///
645    /// Returns [`ParseError::DuplicateAccessVectorRule`] if non-contiguous rules share the same key.
646    pub fn new(rules: AccessVectorRules) -> Result<Self, ParseError> {
647        let hasher = RapidBuildHasher::default();
648        let av_table = build_index(&rules.av_rules, &hasher)?;
649        let type_transition_table = build_index(&rules.type_rules, &hasher)?;
650        let xperms_table = build_index(&rules.xperm_rules, &hasher)?;
651
652        Ok(Self { rules, av_table, type_transition_table, xperms_table, hasher })
653    }
654
655    /// Returns a reference to the underlying unindexed access vector rules.
656    pub fn rules(&self) -> &AccessVectorRules {
657        &self.rules
658    }
659
660    fn find_rules<'a, R: HasRuleKey>(
661        table: &HashTable<U24Index>,
662        rules: &'a [R],
663        key: RuleKey,
664        hasher: &RapidBuildHasher,
665    ) -> impl Iterator<Item = &'a R> {
666        let hash = key.hash(hasher);
667        let slice = match table.find(hash, |&i| rules[usize::from(i)].key() == key) {
668            Some(&i) => &rules[usize::from(i)..],
669            None => &[],
670        };
671        slice.iter().take_while(move |rule| rule.key() == key)
672    }
673
674    /// Returns an iterator yielding matching standard access vector rules for the specified tuple.
675    pub fn find_av_rules(
676        &self,
677        source: TypeId,
678        target: TypeId,
679        class: ClassId,
680    ) -> impl Iterator<Item = &AccessRule> {
681        Self::find_rules(
682            &self.av_table,
683            &self.rules.av_rules,
684            RuleKey::new(source, target, class),
685            &self.hasher,
686        )
687    }
688
689    /// Returns an iterator yielding matching type transition, change, or member rules for the specified tuple.
690    pub fn find_type_rules(
691        &self,
692        source: TypeId,
693        target: TypeId,
694        class: ClassId,
695    ) -> impl Iterator<Item = &TypeRule> {
696        Self::find_rules(
697            &self.type_transition_table,
698            &self.rules.type_rules,
699            RuleKey::new(source, target, class),
700            &self.hasher,
701        )
702    }
703
704    /// Returns an iterator yielding matching extended permission rules for the specified tuple.
705    pub fn find_xperm_rules(
706        &self,
707        source: TypeId,
708        target: TypeId,
709        class: ClassId,
710    ) -> impl Iterator<Item = &XpermRule> {
711        Self::find_rules(
712            &self.xperms_table,
713            &self.rules.xperm_rules,
714            RuleKey::new(source, target, class),
715            &self.hasher,
716        )
717    }
718}
719
720impl Parse for IndexedAccessVectorRules {
721    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
722        let rules = AccessVectorRules::parse(cursor)?;
723        Self::new(rules)
724    }
725}
726
727impl Serialize for IndexedAccessVectorRules {
728    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
729        self.rules.serialize(writer)
730    }
731}
732
733impl Validate for IndexedAccessVectorRules {
734    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
735        self.rules.validate(policy)
736    }
737}
738
739/// Builds a [`HashTable<U24Index>`] mapping [`RuleKey`] hashes to the index of the first rule in each contiguous run.
740///
741/// Binary SELinux policies index rules into buckets by `(source, target, class)`, within which rules are sorted by
742/// `(source, target, class, rule_kind)`. Therefore, all rules sharing a given [`RuleKey`] are guaranteed to be contiguous
743/// and well-ordered.
744///
745/// Returns [`ParseError::DuplicateAccessVectorRule`] if non-contiguous rules share the same key.
746fn build_index<R: HasRuleKey>(
747    rules: &[R],
748    hasher: &RapidBuildHasher,
749) -> Result<HashTable<U24Index>, ParseError> {
750    let mut table = HashTable::new();
751    let mut offset = 0;
752
753    for chunk in rules.chunk_by(|a, b| a.key() == b.key()) {
754        let key = chunk[0].key();
755        let u24_idx = offset.try_into()?;
756        let hash = key.hash(hasher);
757
758        let Entry::Vacant(entry) = table.entry(
759            hash,
760            |&i| rules[usize::from(i)].key() == key,
761            |&i| rules[usize::from(i)].key().hash(hasher),
762        ) else {
763            return Err(ParseError::DuplicateAccessVectorRule { key, kind: chunk[0].kind() });
764        };
765        entry.insert(u24_idx);
766        offset += chunk.len();
767    }
768
769    Ok(table)
770}
771
772/// Expression element kind bit for boolean variable operands.
773pub const COND_EXPR_BOOL: u32 = 1;
774/// Expression element kind bit for unary NOT operator.
775pub const COND_EXPR_NOT: u32 = 2;
776/// Expression element kind bit for binary OR operator.
777pub const COND_EXPR_OR: u32 = 3;
778/// Expression element kind bit for binary AND operator.
779pub const COND_EXPR_AND: u32 = 4;
780/// Expression element kind bit for binary EQUALS operator.
781pub const COND_EXPR_EQ: u32 = 5;
782/// Expression element kind bit for binary NOT-EQUALS operator.
783pub const COND_EXPR_NEQ: u32 = 6;
784
785/// Individual element in a conditional boolean expression sequence.
786#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
787pub enum ConditionalExpressionElement {
788    /// Conditional boolean variable operand.
789    Boolean(ConditionalBooleanId),
790    /// Unary boolean NOT operator.
791    Not,
792    /// Binary boolean OR operator.
793    Or,
794    /// Binary boolean AND operator.
795    And,
796    /// Binary boolean EQUALS operator.
797    Equals,
798    /// Binary boolean NOT-EQUALS operator.
799    NotEquals,
800}
801
802impl Parse for ConditionalExpressionElement {
803    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
804        let expr_type = u32::parse(cursor)?;
805        let boolean_id = u32::parse(cursor)?;
806        match expr_type {
807            COND_EXPR_BOOL => {
808                let id = ConditionalBooleanId::from_u32(boolean_id)
809                    .ok_or(ParseError::InvalidId { value: boolean_id })?;
810                Ok(Self::Boolean(id))
811            }
812            COND_EXPR_NOT => Ok(Self::Not),
813            COND_EXPR_OR => Ok(Self::Or),
814            COND_EXPR_AND => Ok(Self::And),
815            COND_EXPR_EQ => Ok(Self::Equals),
816            COND_EXPR_NEQ => Ok(Self::NotEquals),
817            invalid => Err(ParseError::InvalidEnumValue {
818                enum_name: "ConditionalExpressionElement",
819                value: invalid as u64,
820            }),
821        }
822    }
823}
824
825impl Serialize for ConditionalExpressionElement {
826    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
827        let (expr_type, boolean_id) = match self {
828            Self::Boolean(id) => (COND_EXPR_BOOL, id.as_u32()),
829            Self::Not => (COND_EXPR_NOT, 0),
830            Self::Or => (COND_EXPR_OR, 0),
831            Self::And => (COND_EXPR_AND, 0),
832            Self::Equals => (COND_EXPR_EQ, 0),
833            Self::NotEquals => (COND_EXPR_NEQ, 0),
834        };
835        expr_type.serialize(writer)?;
836        boolean_id.serialize(writer)?;
837        Ok(())
838    }
839}
840
841impl Validate for ConditionalExpressionElement {
842    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
843        if let Self::Boolean(id) = self {
844            id.validate(policy)?;
845        }
846        Ok(())
847    }
848}
849
850/// Parsed SELinux conditional node containing expression AST and true/false branch rule sets.
851#[derive(Clone, Debug, Eq, PartialEq, Parse, Serialize)]
852pub struct ConditionalNode {
853    state: u32,
854    expression_elements: Array<ConditionalExpressionElement>,
855    true_rules: AccessVectorRules,
856    false_rules: AccessVectorRules,
857}
858
859impl ConditionalNode {
860    /// Returns whether this conditional node expression evaluated to active state in policy.
861    pub fn state(&self) -> u32 {
862        self.state
863    }
864
865    /// Returns the expression elements sequence.
866    pub fn expression_elements(&self) -> &[ConditionalExpressionElement] {
867        self.expression_elements.as_ref()
868    }
869
870    /// Returns the true-branch rules for this conditional node.
871    pub fn true_rules(&self) -> &AccessVectorRules {
872        &self.true_rules
873    }
874
875    /// Returns the false-branch rules for this conditional node.
876    pub fn false_rules(&self) -> &AccessVectorRules {
877        &self.false_rules
878    }
879}
880
881impl Validate for ConditionalNode {
882    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
883        self.expression_elements.validate(policy)?;
884        self.true_rules.validate(policy)?;
885        self.false_rules.validate(policy)?;
886        Ok(())
887    }
888}
889
890/// On-wire header identifying the source, target, class, and rule flags of an access vector rule.
891#[derive(Clone, Debug, Eq, PartialEq, Hash, Parse, Serialize)]
892struct BinaryAccessVectorRuleHeader {
893    source_type: u16,
894    target_type: u16,
895    class: u16,
896    rule_flags: u16,
897}
898
899#[cfg(test)]
900mod tests {
901    use super::*;
902    use crate::new_policy::traits::PolicyId;
903
904    #[test]
905    fn test_xperms_bitmap_ops_and_contains() {
906        let mut bitmap1 = XpermsBitmap::NONE;
907        let mut bitmap2 = XpermsBitmap::NONE;
908        assert!(!bitmap1.contains(10));
909        assert!(!bitmap2.contains(10));
910
911        bitmap1.0[0] = 1 << 10;
912        bitmap2.0[0] = 1 << 20;
913        assert!(bitmap1.contains(10));
914        assert!(!bitmap1.contains(20));
915        assert!(bitmap2.contains(20));
916
917        let or_bitmap = bitmap1 | bitmap2;
918        assert!(or_bitmap.contains(10));
919        assert!(or_bitmap.contains(20));
920
921        let and_bitmap = or_bitmap & bitmap1;
922        assert!(and_bitmap.contains(10));
923        assert!(!and_bitmap.contains(20));
924
925        let sub_bitmap = or_bitmap - bitmap1;
926        assert!(!sub_bitmap.contains(10));
927        assert!(sub_bitmap.contains(20));
928
929        let not_bitmap = !XpermsBitmap::NONE;
930        assert_eq!(not_bitmap, XpermsBitmap::ALL);
931        assert!(not_bitmap.contains(0));
932        assert!(not_bitmap.contains(255));
933    }
934
935    #[test]
936    fn test_av_rule_parse_and_serialize() {
937        let data = [
938            1, 0, 0, 0, // count = 1
939            1, 0, // source_type = 1
940            2, 0, // target_type = 2
941            3, 0, // class = 3
942            1, 0, // rule_type = 1 (ALLOW)
943            5, 0, 0, 0, // access_vector = 5
944        ];
945        let mut cursor = PolicyCursor::new(&data);
946        let av_rules = AccessVectorRules::parse(&mut cursor).expect("parse rules table");
947        assert_eq!(av_rules.av_rules.len(), 1);
948        assert_eq!(av_rules.av_rules[0].key.source_type, TypeId::from_u32(1).unwrap());
949        assert_eq!(av_rules.av_rules[0].key.target_type, TypeId::from_u32(2).unwrap());
950        assert_eq!(av_rules.av_rules[0].key.class, ClassId::from_u32(3).unwrap());
951        assert_eq!(av_rules.av_rules[0].access_vector, AccessVector::from(5));
952
953        let mut writer = Vec::new();
954        av_rules.serialize(&mut writer).expect("serialize rules table");
955        assert_eq!(writer.as_slice(), &data);
956    }
957
958    #[test]
959    fn test_av_rule_type_transition_parse_and_serialize() {
960        let data = [
961            1, 0, 0, 0, // count = 1
962            1, 0, // source_type = 1
963            2, 0, // target_type = 2
964            3, 0, // class = 3
965            16, 0, // rule_type = 16 (TYPE_TRANSITION)
966            10, 0, 0, 0, // new_type = 10
967        ];
968        let mut cursor = PolicyCursor::new(&data);
969        let av_rules = AccessVectorRules::parse(&mut cursor).expect("parse rules table");
970        assert_eq!(av_rules.type_rules.len(), 1);
971        assert_eq!(av_rules.type_rules[0].key.source_type, TypeId::from_u32(1).unwrap());
972        assert_eq!(av_rules.type_rules[0].key.target_type, TypeId::from_u32(2).unwrap());
973        assert_eq!(av_rules.type_rules[0].key.class, ClassId::from_u32(3).unwrap());
974        assert_eq!(av_rules.type_rules[0].new_type, TypeId::from_u32(10).unwrap());
975
976        let mut writer = Vec::new();
977        av_rules.serialize(&mut writer).expect("serialize rules table");
978        assert_eq!(writer.as_slice(), &data);
979    }
980
981    #[test]
982    fn test_av_rule_xperm_parse_and_serialize() {
983        let mut data = vec![
984            1, 0, 0, 0, // count = 1
985            1, 0, // source_type = 1
986            2, 0, // target_type = 2
987            3, 0, // class = 3
988            0, 1, // rule_type = 0x0100 (ALLOWXPERM)
989            1, // xperms_type = 1 (XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES)
990            0, // xperms_optional_prefix = 0
991        ];
992        data.extend_from_slice(&[
993            1, 0, 0, 0, 0, 0, 0, 0, // word 0 = 1
994            0, 0, 0, 0, 0, 0, 0, 0, // word 1 = 0
995            0, 0, 0, 0, 0, 0, 0, 0, // word 2 = 0
996            0, 0, 0, 0, 0, 0, 0, 0, // word 3 = 0
997        ]);
998        let mut cursor = PolicyCursor::new(&data);
999        let av_rules = AccessVectorRules::parse(&mut cursor).expect("parse rules table");
1000        assert_eq!(av_rules.xperm_rules.len(), 1);
1001        let xp = &av_rules.xperm_rules[0].extended_permissions;
1002        assert_eq!(xp.xperms_type, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES);
1003        assert!(xp.xperms_bitmap.contains(0));
1004        assert!(!xp.xperms_bitmap.contains(1));
1005
1006        let mut writer = Vec::new();
1007        av_rules.serialize(&mut writer).expect("serialize rules table");
1008        assert_eq!(writer.as_slice(), &data);
1009    }
1010
1011    #[test]
1012    fn test_access_vector_rules_indexing_and_decisions() {
1013        let data = [
1014            2, 0, 0, 0, // count = 2 rules
1015            // Rule 1: ALLOW (source 1, target 2, class 3)
1016            1, 0, // source = 1
1017            2, 0, // target = 2
1018            3, 0, // class = 3
1019            1, 0, // rule_type = 1 (ALLOW)
1020            7, 0, 0, 0, // access_vector = 7
1021            // Rule 2: TYPE_TRANSITION (source 1, target 2, class 3 -> new_type 9)
1022            1, 0, // source = 1
1023            2, 0, // target = 2
1024            3, 0, // class = 3
1025            16, 0, // rule_type = 16 (TYPE_TRANSITION)
1026            9, 0, 0, 0, // new_type = 9
1027        ];
1028
1029        let mut cursor = PolicyCursor::new(&data);
1030        let av_rules = IndexedAccessVectorRules::parse(&mut cursor).expect("parse rules table");
1031
1032        let s1 = TypeId::from_u32(1).unwrap();
1033        let t2 = TypeId::from_u32(2).unwrap();
1034        let c3 = ClassId::from_u32(3).unwrap();
1035
1036        let av_rules_list: Vec<_> = av_rules.find_av_rules(s1, t2, c3).collect();
1037        assert_eq!(av_rules_list.len(), 1);
1038        assert_eq!(av_rules_list[0].kind(), RuleKind::Allow);
1039        assert_eq!(av_rules_list[0].access_vector(), AccessVector::from(7));
1040
1041        let type_rules_list: Vec<_> = av_rules.find_type_rules(s1, t2, c3).collect();
1042        assert_eq!(type_rules_list.len(), 1);
1043        assert_eq!(type_rules_list[0].kind(), RuleKind::TypeTransition);
1044        assert_eq!(type_rules_list[0].new_type(), TypeId::from_u32(9).unwrap());
1045    }
1046
1047    #[test]
1048    fn test_unindexed_access_vector_rules_allows_duplicate_keys() {
1049        let data = [
1050            3, 0, 0, 0, // count = 3 rules
1051            // Rule 1: ALLOW (source 1, target 2, class 3)
1052            1, 0, 2, 0, 3, 0, 1, 0, 7, 0, 0, 0, // access_vector = 7
1053            // Rule 2: ALLOW (source 4, target 5, class 6)
1054            4, 0, 5, 0, 6, 0, 1, 0, 8, 0, 0, 0, // access_vector = 8
1055            // Rule 3: ALLOW (source 1, target 2, class 3) -- non-consecutive duplicate key
1056            1, 0, 2, 0, 3, 0, 1, 0, 9, 0, 0, 0, // access_vector = 9
1057        ];
1058
1059        let mut cursor = PolicyCursor::new(&data);
1060        let av_rules =
1061            AccessVectorRules::parse(&mut cursor).expect("unindexed rules parse duplicate keys");
1062        assert_eq!(av_rules.av_rules.len(), 3);
1063
1064        let mut cursor = PolicyCursor::new(&data);
1065        let err = IndexedAccessVectorRules::parse(&mut cursor)
1066            .expect_err("indexed rules reject duplicate keys");
1067        assert!(matches!(
1068            err,
1069            ParseError::DuplicateAccessVectorRule { key: _, kind: RuleKind::Allow }
1070        ));
1071    }
1072}