Skip to main content

selinux/new_policy/
mod.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
5pub(super) mod access_vector;
6pub(super) mod bitmap;
7pub(super) mod classes;
8pub(super) mod common_symbols;
9pub(super) mod constraints;
10pub(super) mod context;
11pub(super) mod error;
12pub(super) mod id_type;
13pub(super) mod indexed;
14pub(super) mod metadata;
15pub(super) mod mls;
16pub(super) mod parser;
17pub(super) mod permissions;
18pub(super) mod roles;
19pub(super) mod rules;
20pub(super) mod traits;
21pub(super) mod u24_index;
22
23use selinux_policy_derive::{Parse, Serialize, Validate};
24
25use error::{ParseError, ValidateError};
26use metadata::{Config, Counts, Magic, PolicyVersion, Signature};
27pub use metadata::{HandleUnknown, POLICYDB_VERSION_MAX};
28use parser::{Array, PolicyCursor, RemainingBytes};
29use traits::Validate;
30pub use u24_index::U24Index;
31
32pub(super) mod booleans;
33pub(super) mod policy_cap;
34pub(super) mod types;
35pub(super) mod users;
36
37pub use access_vector::AccessVector;
38pub use bitmap::IdSpan;
39pub use booleans::{ConditionalBoolean, ConditionalBooleanId};
40pub use classes::{Class, ClassDefault, ClassDefaultRange, ClassId};
41pub use common_symbols::CommonSymbol;
42pub use constraints::{
43    ConstraintOperator, ConstraintSubject, ConstraintTerm, MlsOperands, MlsOperator, NameExpression,
44};
45pub use context::{Context, MlsLevel, MlsRange};
46pub use id_type::*;
47pub use indexed::IdAndNameIndexed;
48pub use mls::{Category, Sensitivity};
49pub use parser::SymbolArray;
50pub use permissions::PermissionId;
51pub use policy_cap::{PolicyCap, PolicyCapSet};
52pub use roles::{Role, RoleAllow, RoleId, RoleSet, RoleTransition};
53pub use rules::{
54    AccessDecision, AccessVectorRules, ConditionalNode, IndexedAccessVectorRules,
55    SELINUX_AVD_FLAGS_PERMISSIVE, XpermsBitmap,
56};
57pub use types::*;
58pub use users::User;
59
60/// Tag type for type safety of policy user identifiers.
61#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
62pub struct UserTag;
63
64/// Identifies a user within a policy.
65pub type UserId = IdType<std::num::NonZeroU16, UserTag>;
66
67/// Tag type for type safety of policy sensitivity identifiers.
68#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
69pub struct SensitivityTag;
70
71/// Identifies a sensitivity level within a policy.
72pub type SensitivityId = IdType<std::num::NonZeroU16, SensitivityTag>;
73
74/// Tag type for type safety of policy category identifiers.
75#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
76pub struct CategoryTag;
77
78/// Identifies a security category within a policy.
79pub type CategoryId = IdType<std::num::NonZeroU16, CategoryTag>;
80
81/// Set of security categories.
82pub type CategorySet = bitmap::IdSet<CategoryId>;
83
84/// Builder for constructing [`CategorySet`]s dynamically.
85pub type CategorySetBuilder = bitmap::IdSetBuilder<CategoryId>;
86
87/// Top-level [`NewPolicy`] structure that parses the first few fields
88/// and stores the rest in [`Self::rest`] to allow round-trip testing.
89#[derive(Debug, Clone, Parse, Serialize, Validate)]
90pub struct NewPolicy {
91    magic: Magic,
92    signature: Signature,
93    version: PolicyVersion,
94    config: Config,
95    counts: Counts,
96    policy_capabilities: PolicyCapSet,
97    permissive_map: PermissiveTypeSet,
98    common_symbols: IdAndNameIndexed<SymbolArray<CommonSymbol>>,
99    classes: IdAndNameIndexed<SymbolArray<Class>>,
100    roles: IdAndNameIndexed<SymbolArray<Role>>,
101    types: Types,
102    users: IdAndNameIndexed<SymbolArray<User>>,
103    conditional_booleans: IdAndNameIndexed<SymbolArray<ConditionalBoolean>>,
104    sensitivities: IdAndNameIndexed<SymbolArray<Sensitivity>>,
105    categories: IdAndNameIndexed<SymbolArray<Category>>,
106    access_vector_rules: IndexedAccessVectorRules,
107    conditional_nodes: Array<ConditionalNode>,
108    role_transitions: Array<RoleTransition>,
109    role_allowlist: Array<RoleAllow>,
110    rest: RemainingBytes,
111}
112
113impl NewPolicy {
114    /// Parses a [`NewPolicy`] from the raw binary data.
115    pub fn parse(data: &[u8]) -> Result<Self, ParseError> {
116        let mut cursor = PolicyCursor::new(data);
117        cursor.parse()
118    }
119
120    /// Validates the parsed policy.
121    pub fn validate(&self) -> Result<(), ValidateError> {
122        Validate::validate(self, self)
123    }
124
125    /// Returns the policy version.
126    pub fn policy_version(&self) -> u32 {
127        self.version.get()
128    }
129
130    /// Returns the [`HandleUnknown`] configuration.
131    pub fn handle_unknown(&self) -> HandleUnknown {
132        self.config.handle_unknown()
133    }
134
135    /// Returns the policy capabilities set.
136    pub fn policy_capabilities(&self) -> &PolicyCapSet {
137        &self.policy_capabilities
138    }
139
140    /// Returns the permissive types set.
141    pub fn permissive_map(&self) -> &PermissiveTypeSet {
142        &self.permissive_map
143    }
144
145    /// Returns the common symbols table.
146    pub fn common_symbols(&self) -> &IdAndNameIndexed<SymbolArray<CommonSymbol>> {
147        &self.common_symbols
148    }
149
150    /// Returns the object classes table.
151    pub fn classes(&self) -> &IdAndNameIndexed<SymbolArray<Class>> {
152        &self.classes
153    }
154
155    /// Returns the roles table.
156    pub fn roles(&self) -> &IdAndNameIndexed<SymbolArray<Role>> {
157        &self.roles
158    }
159
160    /// Returns the types table.
161    pub fn types(&self) -> &Types {
162        &self.types
163    }
164
165    /// Returns the users table.
166    pub fn users(&self) -> &IdAndNameIndexed<SymbolArray<User>> {
167        &self.users
168    }
169
170    /// Returns the conditional booleans table.
171    pub fn conditional_booleans(&self) -> &IdAndNameIndexed<SymbolArray<ConditionalBoolean>> {
172        &self.conditional_booleans
173    }
174
175    /// Returns the sensitivities table.
176    pub fn sensitivities(&self) -> &IdAndNameIndexed<SymbolArray<Sensitivity>> {
177        &self.sensitivities
178    }
179
180    /// Returns the categories table.
181    pub fn categories(&self) -> &IdAndNameIndexed<SymbolArray<Category>> {
182        &self.categories
183    }
184
185    /// Returns the access vector rules table.
186    pub fn access_vector_rules(&self) -> &IndexedAccessVectorRules {
187        &self.access_vector_rules
188    }
189
190    /// Returns the list of conditional nodes.
191    pub fn conditional_nodes(&self) -> &[ConditionalNode] {
192        self.conditional_nodes.as_ref()
193    }
194
195    /// Returns the role transitions rules array.
196    pub(crate) fn role_transitions(&self) -> &[RoleTransition] {
197        self.role_transitions.as_ref()
198    }
199
200    /// Returns the role allow rules array.
201    pub(crate) fn role_allowlist(&self) -> &[RoleAllow] {
202        self.role_allowlist.as_ref()
203    }
204
205    /// Returns a shared reference to the remaining unparsed bytes.
206    pub fn rest_bytes(&self) -> std::sync::Arc<[u8]> {
207        self.rest.bytes.clone()
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use crate::new_policy::traits::{HasName, Parse, Serialize};
215
216    #[derive(Copy, Clone, Debug, Eq, PartialEq, Parse, Serialize, Validate)]
217    #[policy(wire_type = u32)]
218    enum TestEnum {
219        ValueOne = 1,
220        ValueTwo = 2,
221    }
222
223    #[test]
224    fn test_enum_derive() {
225        let mut cursor = PolicyCursor::new(&[1, 0, 0, 0]);
226        let parsed = TestEnum::parse(&mut cursor).unwrap();
227        assert_eq!(parsed, TestEnum::ValueOne);
228
229        let mut cursor = PolicyCursor::new(&[2, 0, 0, 0]);
230        let parsed = TestEnum::parse(&mut cursor).unwrap();
231        assert_eq!(parsed, TestEnum::ValueTwo);
232
233        let mut cursor = PolicyCursor::new(&[3, 0, 0, 0]);
234        let err = TestEnum::parse(&mut cursor).unwrap_err();
235        assert!(matches!(err, ParseError::InvalidEnumValue { enum_name: "TestEnum", value: 3 }));
236
237        let mut writer = Vec::new();
238        TestEnum::ValueOne.serialize(&mut writer).unwrap();
239        assert_eq!(writer, vec![1, 0, 0, 0]);
240
241        let mut writer = Vec::new();
242        TestEnum::ValueTwo.serialize(&mut writer).unwrap();
243        assert_eq!(writer, vec![2, 0, 0, 0]);
244
245        let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
246        let policy = NewPolicy::parse(policy_bytes).unwrap();
247        TestEnum::ValueOne.validate(&policy).unwrap();
248    }
249
250    #[test]
251    fn test_real_policy_roundtrip() {
252        let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
253        let new_policy = NewPolicy::parse(policy_bytes).unwrap();
254        new_policy.validate().unwrap();
255
256        // Verify metadata basics
257        assert!(new_policy.policy_version() >= 30);
258        assert_eq!(new_policy.handle_unknown(), HandleUnknown::Allow);
259
260        // Verify that we can query policy capabilities and permissive map
261        // (even if they are empty or have specific values in the test policy,
262        // we just verify the APIs exist and don't panic).
263        let _caps = new_policy.policy_capabilities();
264        let _permissive = new_policy.permissive_map();
265
266        // Verify common symbols are parsed
267        assert!(!new_policy.common_symbols().is_empty());
268        let common = &new_policy.common_symbols()[0];
269        assert!(!common.name().is_empty());
270        assert!(!common.permissions().is_empty());
271
272        // Verify classes are parsed
273        assert!(!new_policy.classes().is_empty());
274        let class = &new_policy.classes()[0];
275        assert!(!class.name().is_empty());
276
277        // Verify types are parsed
278        assert!(!new_policy.types().is_empty());
279        let t = &new_policy.types().iter().next().unwrap();
280        assert!(!t.name().is_empty());
281
282        // Verify users are parsed
283        assert!(!new_policy.users().is_empty());
284        let u = &new_policy.users()[0];
285        assert!(!u.name().is_empty());
286
287        // Verify conditional booleans are parsed
288        assert!(!new_policy.conditional_booleans().is_empty());
289        let b = &new_policy.conditional_booleans()[0];
290        assert!(!b.name().is_empty());
291
292        // Verify sensitivities are parsed
293        assert!(!new_policy.sensitivities().is_empty());
294        let s = &new_policy.sensitivities()[0];
295        assert!(!s.name().is_empty());
296
297        // Verify categories are parsed
298        assert!(!new_policy.categories().is_empty());
299        let c = &new_policy.categories()[0];
300        assert!(!c.name().is_empty());
301
302        // Verify conditional nodes are parsed
303        let _nodes = new_policy.conditional_nodes();
304
305        // Verify 100% byte-for-byte roundtrip fidelity
306        let mut serialized = Vec::new();
307        new_policy.serialize(&mut serialized).unwrap();
308        assert_eq!(serialized.len(), policy_bytes.len());
309        assert_eq!(serialized, policy_bytes);
310    }
311}