Skip to main content

selinux/new_policy/
types.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::NonZeroU16;
6
7use hashbrown::HashTable;
8use selinux_policy_derive::{HasPolicyId, Parse, Serialize};
9
10use super::bitmap::IdSet;
11use super::error::{ParseError, SerializeError, ValidateError};
12use super::id_type::IdType;
13use super::indexed::hash_name;
14use super::parser::{Array, PolicyCursor};
15use super::traits::{HasName, Parse, PolicyId, Serialize, Validate};
16use super::{NewPolicy, U24Index};
17
18/// Tag type for type safety of policy type identifiers.
19#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
20pub struct TypeTag;
21
22/// Identifies a type (or type attribute) within a policy.
23pub type TypeId = IdType<NonZeroU16, TypeTag>;
24
25/// Set of types that are marked permissive.
26pub type PermissiveTypeSet = IdSet<TypeId, true>;
27
28/// Set of [`TypeId`]s.
29pub type TypeSet = IdSet<TypeId>;
30
31impl Validate for TypeId {
32    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
33        policy
34            .types()
35            .get_by_id(*self)
36            .map(|_| ())
37            .ok_or_else(|| ValidateError::UnknownId { kind: "type", id: self.as_u32() })
38    }
39}
40
41#[derive(Copy, Clone, Debug, PartialEq, Eq)]
42pub enum TypeKind {
43    Alias,
44    Type,
45    Attribute,
46}
47
48impl TypeKind {
49    pub const ALIAS: u32 = 0;
50    pub const TYPE: u32 = 1;
51    pub const ATTRIBUTE: u32 = 3;
52}
53
54/// Parsed SELinux type, containing an ID, a name, properties, and optional bounds.
55#[derive(Debug, Clone, PartialEq, Eq, HasPolicyId)]
56pub struct Type {
57    pub(super) id: TypeId,
58    pub(super) name: Box<[u8]>,
59    pub(super) properties: TypeKind,
60    pub(super) bounds: Option<TypeId>,
61}
62
63impl HasName for Type {
64    fn name(&self) -> &[u8] {
65        &self.name
66    }
67}
68
69impl Type {
70    pub fn bounded_by(&self) -> Option<TypeId> {
71        self.bounds
72    }
73}
74
75#[derive(Parse, Serialize)]
76struct BinaryTypeMetadata {
77    length: u32,
78    id: u32,
79    properties: u32,
80    bounds: u32,
81}
82
83impl Parse for Type {
84    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
85        let metadata = BinaryTypeMetadata::parse(cursor)?;
86        let name = cursor.read_bytes(metadata.length as usize)?.to_vec().into_boxed_slice();
87
88        let properties_val = metadata.properties;
89        let properties = match properties_val {
90            TypeKind::ALIAS => TypeKind::Alias,
91            TypeKind::TYPE => TypeKind::Type,
92            TypeKind::ATTRIBUTE => TypeKind::Attribute,
93            v => {
94                return Err(ParseError::InvalidEnumValue {
95                    enum_name: "TypeKind",
96                    value: v as u64,
97                });
98            }
99        };
100
101        let bounds = TypeId::from_u32(metadata.bounds);
102        let id =
103            TypeId::from_u32(metadata.id).ok_or(ParseError::InvalidId { value: metadata.id })?;
104
105        Ok(Self { id, name, properties, bounds })
106    }
107}
108
109impl Serialize for Type {
110    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
111        let properties_val = match self.properties {
112            TypeKind::Alias => TypeKind::ALIAS,
113            TypeKind::Type => TypeKind::TYPE,
114            TypeKind::Attribute => TypeKind::ATTRIBUTE,
115        };
116        let metadata = BinaryTypeMetadata {
117            length: self.name.len() as u32,
118            id: self.id.as_u32(),
119            properties: properties_val,
120            bounds: self.bounds.map_or(0, |id| id.as_u32()),
121        };
122        metadata.serialize(writer)?;
123        writer.extend_from_slice(&self.name);
124        Ok(())
125    }
126}
127
128impl Validate for Type {
129    fn validate(&self, _policy: &NewPolicy) -> Result<(), ValidateError> {
130        // Structural validation is done during parsing.
131        Ok(())
132    }
133}
134
135/// Container for all types in the policy, providing indices for fast lookup by ID and Name.
136#[derive(Debug, Clone)]
137pub struct Types {
138    primary_names_count: u32,
139    /// In-order list of all types, attributes, and aliases.
140    ordered: Array<Type>,
141
142    /// Maps TypeId -> index in `ordered`. Only contains Types and Attributes.
143    /// Index is `TypeId - 1`.
144    by_id: Box<[Option<U24Index>]>,
145
146    /// Maps name -> index in `ordered`. Only contains Types and Aliases.
147    by_name: HashTable<U24Index>,
148    hasher: rapidhash::RapidBuildHasher,
149}
150
151impl PartialEq for Types {
152    fn eq(&self, other: &Self) -> bool {
153        self.primary_names_count == other.primary_names_count && self.ordered == other.ordered
154    }
155}
156
157impl Eq for Types {}
158
159impl Parse for Types {
160    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
161        let primary_names_count = u32::parse(cursor)?;
162        let ordered = Array::<Type>::parse(cursor)?;
163
164        // Build indices
165        let mut by_id = Vec::new();
166        let hasher = rapidhash::RapidBuildHasher::default();
167        let mut by_name = HashTable::new();
168
169        for (index, t) in ordered.iter().enumerate() {
170            let u24_idx: U24Index = index.try_into()?;
171            if t.properties == TypeKind::Type || t.properties == TypeKind::Attribute {
172                let id = t.id.as_u32() as usize;
173                if id > by_id.len() {
174                    by_id.resize(id, None);
175                }
176                by_id[id - 1] = Some(u24_idx);
177            }
178            if t.properties == TypeKind::Type || t.properties == TypeKind::Alias {
179                let hash = hash_name(&hasher, t.name.as_ref());
180                by_name.insert_unique(hash, u24_idx, |&idx| {
181                    hash_name(&hasher, ordered[idx].name.as_ref())
182                });
183            }
184        }
185
186        Ok(Self { primary_names_count, ordered, by_id: by_id.into_boxed_slice(), by_name, hasher })
187    }
188}
189
190impl Serialize for Types {
191    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
192        self.primary_names_count.serialize(writer)?;
193        self.ordered.serialize(writer)
194    }
195}
196
197impl Validate for Types {
198    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
199        for t in self.ordered.iter() {
200            t.validate(policy)?;
201        }
202        Ok(())
203    }
204}
205
206impl Types {
207    pub fn primary_names_count(&self) -> u32 {
208        self.primary_names_count
209    }
210
211    pub fn get_by_id(&self, id: TypeId) -> Option<&Type> {
212        let index = self.by_id.get((id.as_u32() - 1) as usize)?.as_ref()?;
213        Some(&self.ordered[*index])
214    }
215
216    pub fn get_by_name(&self, name: &[u8]) -> Option<&Type> {
217        let hash = hash_name(&self.hasher, name);
218        let idx = self.by_name.find(hash, |&idx| self.ordered[idx].name.as_ref() == name)?;
219        Some(&self.ordered[*idx])
220    }
221
222    pub fn is_empty(&self) -> bool {
223        self.ordered.is_empty()
224    }
225
226    pub fn iter(&self) -> impl Iterator<Item = &Type> {
227        self.ordered.iter()
228    }
229}