1pub(super) mod access_vector;
6pub(super) mod bitmap;
7pub(super) mod booleans;
8pub(super) mod classes;
9pub(super) mod common_symbols;
10pub(super) mod constraints;
11pub(super) mod context;
12pub(super) mod error;
13pub(super) mod filename_transitions;
14pub(super) mod id_type;
15pub(super) mod indexed;
16pub(super) mod initial_sids;
17pub(super) mod metadata;
18pub(super) mod mls;
19pub(super) mod object_contexts;
20pub(super) mod parser;
21pub(super) mod permissions;
22pub(super) mod policy_cap;
23pub(super) mod roles;
24pub(super) mod rules;
25pub(super) mod traits;
26pub(super) mod types;
27pub(super) mod u24_index;
28pub(super) mod users;
29
30use selinux_policy_derive::{Parse, Serialize, Validate};
31
32pub use access_vector::AccessVector;
33pub use bitmap::IdSpan;
34pub use booleans::{ConditionalBoolean, ConditionalBooleanId};
35pub use classes::{Class, ClassDefault, ClassDefaultRange, ClassId};
36pub use common_symbols::CommonSymbol;
37pub use constraints::{
38 ConstraintOperator, ConstraintSubject, ConstraintTerm, MlsOperands, MlsOperator, NameExpression,
39};
40pub use context::{Context, MlsLevel, MlsRange};
41use error::{ParseError, SerializeError, ValidateError};
42pub use filename_transitions::FilenameTransitions;
43pub use id_type::*;
44pub use indexed::IdAndNameIndexed;
45pub use initial_sids::InitialSids;
46use metadata::{Config, Counts, Magic, Signature};
47pub use metadata::{HandleUnknown, POLICYDB_VERSION_MAX, PolicyVersion};
48pub use mls::{Category, RangeTransition, Sensitivity};
49pub use object_contexts::{FsUseType, GenfsCon, GenfsConPath, ObjectContexts};
50use parser::{Array, PolicyCursor};
51pub use parser::{PolicyWriter, SymbolArray};
52pub use permissions::PermissionId;
53pub use policy_cap::{PolicyCap, PolicyCapSet};
54pub use roles::{Role, RoleAllow, RoleId, RoleSet, RoleTransition};
55pub use rules::{
56 AccessDecision, AccessVectorRules, ConditionalNode, IndexedAccessVectorRules,
57 SELINUX_AVD_FLAGS_PERMISSIVE, XpermsBitmap,
58};
59use traits::{Serialize, Validate};
60pub use types::*;
61pub use u24_index::U24Index;
62pub use users::User;
63
64#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
66pub struct UserTag;
67
68pub type UserId = IdType<std::num::NonZeroU16, UserTag>;
70
71#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
73pub struct SensitivityTag;
74
75pub type SensitivityId = IdType<std::num::NonZeroU16, SensitivityTag>;
77
78#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
80pub struct CategoryTag;
81
82pub type CategoryId = IdType<std::num::NonZeroU16, CategoryTag>;
84
85pub type CategorySet = bitmap::IdSet<CategoryId>;
87
88pub type CategorySetBuilder = bitmap::IdSetBuilder<CategoryId>;
90
91#[derive(Debug, Parse, Serialize, Validate)]
93pub struct NewPolicy {
94 magic: Magic,
95 signature: Signature,
96 version: PolicyVersion,
97 config: Config,
98 counts: Counts,
99 policy_capabilities: PolicyCapSet,
100 permissive_map: PermissiveTypeSet,
101 common_symbols: IdAndNameIndexed<SymbolArray<CommonSymbol>>,
102 classes: IdAndNameIndexed<SymbolArray<Class>>,
103 roles: IdAndNameIndexed<SymbolArray<Role>>,
104 types: Types,
105 users: IdAndNameIndexed<SymbolArray<User>>,
106 conditional_booleans: IdAndNameIndexed<SymbolArray<ConditionalBoolean>>,
107 sensitivities: IdAndNameIndexed<SymbolArray<Sensitivity>>,
108 categories: IdAndNameIndexed<SymbolArray<Category>>,
109 access_vector_rules: IndexedAccessVectorRules,
110 conditional_nodes: Array<ConditionalNode>,
111 role_transitions: Array<RoleTransition>,
112 role_allowlist: Array<RoleAllow>,
113 filename_transitions: FilenameTransitions,
114 initial_sids: InitialSids,
115 object_contexts: ObjectContexts,
116 generic_fs_contexts: Array<GenfsCon>,
117 range_transitions: Array<RangeTransition>,
118 type_attribute_maps: TypeAttributeMaps,
119}
120
121impl NewPolicy {
122 pub fn parse(data: &[u8]) -> Result<Self, ParseError> {
124 let mut cursor = PolicyCursor::new(data);
125 let policy = cursor.parse()?;
126 if cursor.offset() < data.len() {
127 return Err(ParseError::TrailingBytes { num_bytes: data.len() - cursor.offset() });
128 }
129 Ok(policy)
130 }
131
132 pub fn validate(&self) -> Result<(), ValidateError> {
134 Validate::validate(self, self)
135 }
136
137 pub fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
139 let mut policy_writer = PolicyWriter::new(self.version, writer);
140 Serialize::serialize(self, &mut policy_writer)
141 }
142
143 pub fn version(&self) -> PolicyVersion {
145 self.version
146 }
147
148 pub fn handle_unknown(&self) -> HandleUnknown {
150 self.config.handle_unknown()
151 }
152
153 pub fn policy_capabilities(&self) -> &PolicyCapSet {
155 &self.policy_capabilities
156 }
157
158 pub fn permissive_map(&self) -> &PermissiveTypeSet {
160 &self.permissive_map
161 }
162
163 pub fn common_symbols(&self) -> &IdAndNameIndexed<SymbolArray<CommonSymbol>> {
165 &self.common_symbols
166 }
167
168 pub fn classes(&self) -> &IdAndNameIndexed<SymbolArray<Class>> {
170 &self.classes
171 }
172
173 pub fn roles(&self) -> &IdAndNameIndexed<SymbolArray<Role>> {
175 &self.roles
176 }
177
178 pub fn types(&self) -> &Types {
180 &self.types
181 }
182
183 pub fn users(&self) -> &IdAndNameIndexed<SymbolArray<User>> {
185 &self.users
186 }
187
188 pub fn conditional_booleans(&self) -> &IdAndNameIndexed<SymbolArray<ConditionalBoolean>> {
190 &self.conditional_booleans
191 }
192
193 pub fn sensitivities(&self) -> &IdAndNameIndexed<SymbolArray<Sensitivity>> {
195 &self.sensitivities
196 }
197
198 pub fn categories(&self) -> &IdAndNameIndexed<SymbolArray<Category>> {
200 &self.categories
201 }
202
203 pub fn access_vector_rules(&self) -> &IndexedAccessVectorRules {
205 &self.access_vector_rules
206 }
207
208 #[cfg(test)]
210 pub(crate) fn conditional_nodes(&self) -> &[ConditionalNode] {
211 self.conditional_nodes.as_ref()
212 }
213
214 pub(crate) fn role_transitions(&self) -> &[RoleTransition] {
216 self.role_transitions.as_ref()
217 }
218
219 pub(crate) fn role_allowlist(&self) -> &[RoleAllow] {
221 self.role_allowlist.as_ref()
222 }
223
224 pub fn filename_transitions(&self) -> &FilenameTransitions {
226 &self.filename_transitions
227 }
228
229 pub fn initial_sids(&self) -> &InitialSids {
231 &self.initial_sids
232 }
233
234 pub fn object_contexts(&self) -> &ObjectContexts {
236 &self.object_contexts
237 }
238
239 pub(crate) fn generic_fs_contexts(&self) -> &[GenfsCon] {
241 &self.generic_fs_contexts
242 }
243
244 pub(crate) fn range_transitions(&self) -> &[RangeTransition] {
246 &self.range_transitions
247 }
248
249 pub fn type_attribute_maps(&self) -> &TypeAttributeMaps {
251 &self.type_attribute_maps
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::new_policy::traits::{Parse, Serialize};
259
260 #[derive(Copy, Clone, Debug, Eq, PartialEq, Parse, Serialize, Validate)]
261 #[policy(wire_type = u32)]
262 enum TestEnum {
263 ValueOne = 1,
264 ValueTwo = 2,
265 }
266
267 #[test]
268 fn test_enum_derive() {
269 let mut cursor = PolicyCursor::new(&[1, 0, 0, 0]);
270 let parsed = TestEnum::parse(&mut cursor).unwrap();
271 assert_eq!(parsed, TestEnum::ValueOne);
272
273 let mut cursor = PolicyCursor::new(&[2, 0, 0, 0]);
274 let parsed = TestEnum::parse(&mut cursor).unwrap();
275 assert_eq!(parsed, TestEnum::ValueTwo);
276
277 let mut cursor = PolicyCursor::new(&[3, 0, 0, 0]);
278 let err = TestEnum::parse(&mut cursor).unwrap_err();
279 assert!(matches!(err, ParseError::InvalidEnumValue { enum_name: "TestEnum", value: 3 }));
280
281 let mut writer = Vec::new();
282 let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
283 TestEnum::ValueOne.serialize(&mut policy_writer).unwrap();
284 assert_eq!(writer, vec![1, 0, 0, 0]);
285
286 let mut writer = Vec::new();
287 let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
288 TestEnum::ValueTwo.serialize(&mut policy_writer).unwrap();
289 assert_eq!(writer, vec![2, 0, 0, 0]);
290
291 let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
292 let policy = NewPolicy::parse(policy_bytes).unwrap();
293 TestEnum::ValueOne.validate(&policy).unwrap();
294 }
295
296 const TEST_POLICIES: &[(&str, &[u8])] = &[
297 ("selinux_testsuite", include_bytes!("../../testdata/policies/selinux_testsuite")),
298 ("emulator", include_bytes!("../../testdata/policies/emulator")),
299 (
300 "conditional_policy",
301 include_bytes!("../../testdata/composite_policies/compiled/conditional_policy"),
302 ),
303 (
304 "minimal_policy",
305 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy"),
306 ),
307 (
308 "allow_fork_policy",
309 include_bytes!("../../testdata/composite_policies/compiled/allow_fork_policy"),
310 ),
311 (
312 "class_defaults_policy",
313 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy"),
314 ),
315 (
316 "genfscon_policy",
317 include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy"),
318 ),
319 (
320 "handle_unknown_policy-allow",
321 include_bytes!(
322 "../../testdata/composite_policies/compiled/handle_unknown_policy-allow"
323 ),
324 ),
325 (
326 "handle_unknown_policy-deny",
327 include_bytes!("../../testdata/composite_policies/compiled/handle_unknown_policy-deny"),
328 ),
329 (
330 "handle_unknown_policy-reject",
331 include_bytes!(
332 "../../testdata/composite_policies/compiled/handle_unknown_policy-reject"
333 ),
334 ),
335 (
336 "range_transition_policy",
337 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy"),
338 ),
339 (
340 "role_transition_policy",
341 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy"),
342 ),
343 (
344 "type_transition_policy",
345 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy"),
346 ),
347 ];
348
349 #[test]
350 fn test_all_compiled_policies_roundtrip() {
351 for (name, policy_bytes) in TEST_POLICIES {
352 let new_policy = NewPolicy::parse(policy_bytes)
353 .unwrap_or_else(|e| panic!("Failed to parse {name}: {e:?}"));
354 new_policy.validate().unwrap_or_else(|e| panic!("Failed to validate {name}: {e:?}"));
355
356 if new_policy.version() >= PolicyVersion::V33 {
357 let mut serialized = Vec::new();
358 new_policy
359 .serialize(&mut serialized)
360 .unwrap_or_else(|e| panic!("Failed to serialize {name}: {e:?}"));
361 assert_bytes_eq(&serialized, policy_bytes);
362 }
363 }
364 }
365
366 #[test]
367 fn test_initial_sids_policy_elements() {
368 let policy_bytes =
369 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy");
370 let new_policy = NewPolicy::parse(policy_bytes).expect("parse minimal policy");
371 new_policy.validate().expect("validate minimal policy");
372
373 assert!(!new_policy.initial_sids().is_empty());
374 assert!(new_policy.initial_sids().len() >= crate::InitialSid::all_variants().len() - 1);
375
376 let kernel_context =
377 new_policy.initial_sids().get_by_id(crate::InitialSid::Kernel as u32).unwrap();
378 assert!(new_policy.users().get_by_id(kernel_context.user()).is_some());
379 assert!(new_policy.roles().get_by_id(kernel_context.role()).is_some());
380
381 let unlabeled_context =
382 new_policy.initial_sids().get_by_id(crate::InitialSid::Unlabeled as u32).unwrap();
383 assert!(new_policy.users().get_by_id(unlabeled_context.user()).is_some());
384 assert!(new_policy.roles().get_by_id(unlabeled_context.role()).is_some());
385 }
386}
387
388#[cfg(test)]
389pub(crate) fn assert_bytes_eq(left: &[u8], right: &[u8]) {
390 if left != right {
391 let min_len = std::cmp::min(left.len(), right.len());
392 for i in 0..min_len {
393 if left[i] != right[i] {
394 let start = i.saturating_sub(8);
395 let end = std::cmp::min(i + 16, min_len);
396 panic!(
397 "Byte mismatch at offset {i} (0x{i:x}): actual=0x{:02x} vs expected=0x{:02x}.\nActual [{start}..{end}]: {:02x?}\nExpected [{start}..{end}]: {:02x?}",
398 left[i],
399 right[i],
400 &left[start..end],
401 &right[start..end]
402 );
403 }
404 }
405 panic!("Length mismatch: actual={}, expected={}", left.len(), right.len());
406 }
407}