1use super::arrays::{FsContext, FsUseType};
6use super::metadata::HandleUnknown;
7use super::parser::ParseStrategy;
8use super::security_context::{SecurityContext, SecurityLevel};
9use super::symbols::{
10 Class, ClassDefault, ClassDefaultRange, Classes, CommonSymbol, CommonSymbols, Permission,
11};
12use super::{ClassId, ParsedPolicy, RoleId, TypeId};
13
14use crate::{ClassPermission as _, FileClass, FsNodeClass, NullessByteStr};
15use std::collections::HashMap;
16
17pub struct FsUseLabelAndType {
19 pub context: SecurityContext,
20 pub use_type: FsUseType,
21}
22
23#[derive(Debug)]
31pub(super) struct PolicyIndex<PS: ParseStrategy> {
32 classes: HashMap<crate::ObjectClass, usize>,
35 permissions: HashMap<crate::Permission, PermissionIndex>,
38 parsed_policy: ParsedPolicy<PS>,
40 cached_object_r_role: RoleId,
42}
43
44impl<PS: ParseStrategy> PolicyIndex<PS> {
45 pub fn new(parsed_policy: ParsedPolicy<PS>) -> Result<Self, anyhow::Error> {
52 let policy_classes = parsed_policy.classes();
53 let common_symbols = parsed_policy.common_symbols();
54
55 let mut classes = HashMap::new();
58 for known_class in crate::ObjectClass::all_variants().into_iter() {
59 match get_class_index_by_name(policy_classes, known_class.name()) {
60 Some(class_index) => {
61 classes.insert(known_class, class_index);
62 }
63 None => {
64 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
65 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
66 }
67 }
68 }
69 }
70
71 let mut permissions = HashMap::new();
74 for known_permission in crate::Permission::all_variants().into_iter() {
75 let object_class = known_permission.class();
76 if let Some(class_index) = classes.get(&object_class) {
77 let class = &policy_classes[*class_index];
78 if let Some(permission_index) =
79 get_permission_index_by_name(common_symbols, class, known_permission.name())
80 {
81 permissions.insert(known_permission, permission_index);
82 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
83 return Err(anyhow::anyhow!(
84 "missing permission {:?}:{:?}",
85 object_class.name(),
86 known_permission.name(),
87 ));
88 }
89 }
90 }
91
92 let cached_object_r_role = parsed_policy
94 .role_by_name("object_r".into())
95 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
96 .id();
97
98 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
99
100 for id in crate::InitialSid::all_variants() {
102 index.resolve_initial_context(id);
103 }
104
105 for fs_use in index.parsed_policy.fs_uses() {
107 SecurityContext::new_from_policy_context(fs_use.context());
108 }
109
110 Ok(index)
111 }
112
113 pub fn class<'a>(&'a self, object_class: &crate::ObjectClass) -> Option<&'a Class<PS>> {
114 self.classes.get(object_class).map(|offset| &self.parsed_policy.classes()[*offset])
115 }
116
117 pub fn permission<'a>(&'a self, permission: &crate::Permission) -> Option<&'a Permission<PS>> {
118 let target_class = self.class(&permission.class())?;
119 self.permissions.get(permission).map(|p| match p {
120 PermissionIndex::Class { permission_index } => {
121 &target_class.permissions()[*permission_index]
122 }
123 PermissionIndex::Common { common_symbol_index, permission_index } => {
124 let common_symbol = &self.parsed_policy().common_symbols()[*common_symbol_index];
125 &common_symbol.permissions()[*permission_index]
126 }
127 })
128 }
129
130 pub fn new_file_security_context(
135 &self,
136 source: &SecurityContext,
137 target: &SecurityContext,
138 class: &crate::FsNodeClass,
139 ) -> SecurityContext {
140 let object_class = crate::ObjectClass::from(class.clone());
141 let default_role = match class {
142 FsNodeClass::Socket(_) => source.role(),
144 FsNodeClass::File(file) if *file == FileClass::Fifo => source.role(),
145 FsNodeClass::File(_) => self.cached_object_r_role,
148 };
149 self.new_security_context(
150 source,
151 target,
152 &object_class,
153 default_role,
154 target.type_(),
157 source.low_level(),
160 None,
161 )
162 }
163
164 pub fn new_file_security_context_by_name(
170 &self,
171 source: &SecurityContext,
172 target: &SecurityContext,
173 class: &crate::FsNodeClass,
174 name: NullessByteStr<'_>,
175 ) -> Option<SecurityContext> {
176 let object_class = crate::ObjectClass::from(class.clone());
177 let policy_class = self.class(&object_class)?;
178 let type_id = self.type_transition_new_type_with_name(
179 source.type_(),
180 target.type_(),
181 policy_class,
182 name,
183 )?;
184 Some(self.new_security_context_internal(
185 source,
186 target,
187 &object_class,
188 self.cached_object_r_role,
190 target.type_(),
193 source.low_level(),
196 None,
197 Some(type_id),
199 ))
200 }
201
202 pub fn new_security_context(
222 &self,
223 source: &SecurityContext,
224 target: &SecurityContext,
225 class: &crate::ObjectClass,
226 default_role: RoleId,
227 default_type: TypeId,
228 default_low_level: &SecurityLevel,
229 default_high_level: Option<&SecurityLevel>,
230 ) -> SecurityContext {
231 self.new_security_context_internal(
232 source,
233 target,
234 class,
235 default_role,
236 default_type,
237 default_low_level,
238 default_high_level,
239 None,
240 )
241 }
242
243 fn new_security_context_internal(
249 &self,
250 source: &SecurityContext,
251 target: &SecurityContext,
252 class: &crate::ObjectClass,
253 default_role: RoleId,
254 default_type: TypeId,
255 default_low_level: &SecurityLevel,
256 default_high_level: Option<&SecurityLevel>,
257 override_type: Option<TypeId>,
258 ) -> SecurityContext {
259 let (user, role, type_, low_level, high_level) = if let Some(policy_class) =
260 self.class(&class)
261 {
262 let class_defaults = policy_class.defaults();
263
264 let user = match class_defaults.user() {
265 ClassDefault::Source => source.user(),
266 ClassDefault::Target => target.user(),
267 _ => source.user(),
268 };
269
270 let role =
271 match self.role_transition_new_role(source.role(), target.type_(), policy_class) {
272 Some(new_role) => new_role,
273 None => match class_defaults.role() {
274 ClassDefault::Source => source.role(),
275 ClassDefault::Target => target.role(),
276 _ => default_role,
277 },
278 };
279
280 let type_ = override_type.unwrap_or_else(|| {
281 match self.type_transition_new_type(source.type_(), target.type_(), policy_class) {
282 Some(new_type) => new_type,
283 None => match class_defaults.type_() {
284 ClassDefault::Source => source.type_(),
285 ClassDefault::Target => target.type_(),
286 _ => default_type,
287 },
288 }
289 });
290
291 let (low_level, high_level) =
292 match self.range_transition_new_range(source.type_(), target.type_(), policy_class)
293 {
294 Some((low_level, high_level)) => (low_level, high_level),
295 None => match class_defaults.range() {
296 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
297 ClassDefaultRange::SourceHigh => (
298 source.high_level().unwrap_or_else(|| source.low_level()).clone(),
299 None,
300 ),
301 ClassDefaultRange::SourceLowHigh => {
302 (source.low_level().clone(), source.high_level().map(Clone::clone))
303 }
304 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
305 ClassDefaultRange::TargetHigh => (
306 target.high_level().unwrap_or_else(|| target.low_level()).clone(),
307 None,
308 ),
309 ClassDefaultRange::TargetLowHigh => {
310 (target.low_level().clone(), target.high_level().map(Clone::clone))
311 }
312 _ => (default_low_level.clone(), default_high_level.map(Clone::clone)),
313 },
314 };
315
316 (user, role, type_, low_level, high_level)
317 } else {
318 (
321 source.user(),
322 default_role,
323 default_type,
324 default_low_level.clone(),
325 default_high_level.map(Clone::clone),
326 )
327 };
328
329 SecurityContext::new(user, role, type_, low_level, high_level)
331
332 }
334
335 pub(super) fn object_role(&self) -> RoleId {
338 self.cached_object_r_role
339 }
340
341 pub(super) fn parsed_policy(&self) -> &ParsedPolicy<PS> {
342 &self.parsed_policy
343 }
344
345 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
348 self.resolve_initial_context(id)
350 }
351
352 pub(super) fn fs_use_label_and_type(
355 &self,
356 fs_type: NullessByteStr<'_>,
357 ) -> Option<FsUseLabelAndType> {
358 self.parsed_policy
359 .fs_uses()
360 .iter()
361 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
362 .map(|fs_use| FsUseLabelAndType {
363 context: SecurityContext::new_from_policy_context(fs_use.context()),
364 use_type: fs_use.behavior(),
365 })
366 }
367
368 pub(super) fn genfscon_label_for_fs_and_path(
373 &self,
374 fs_type: NullessByteStr<'_>,
375 node_path: NullessByteStr<'_>,
376 class_id: Option<ClassId>,
377 ) -> Option<SecurityContext> {
378 let fs_contexts = self
380 .parsed_policy
381 .generic_fs_contexts()
382 .iter()
383 .find(|genfscon| genfscon.fs_type() == fs_type.as_bytes())?
384 .contexts();
385
386 let mut result: Option<&FsContext<PS>> = None;
399 for fs_context in fs_contexts {
400 if node_path.0.starts_with(fs_context.partial_path()) {
401 if result.is_none()
402 || result.unwrap().partial_path().len() < fs_context.partial_path().len()
403 {
404 if class_id.is_none()
405 || fs_context
406 .class()
407 .map(|other| other == class_id.unwrap())
408 .unwrap_or(true)
409 {
410 result = Some(fs_context);
411 }
412 }
413 }
414 }
415
416 result.and_then(|fs_context| {
419 Some(SecurityContext::new_from_policy_context(fs_context.context()))
420 })
421 }
422
423 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
425 SecurityContext::new_from_policy_context(self.parsed_policy().initial_context(id))
426 }
427
428 fn role_transition_new_role(
429 &self,
430 current_role: RoleId,
431 type_: TypeId,
432 class: &Class<PS>,
433 ) -> Option<RoleId> {
434 self.parsed_policy
435 .role_transitions()
436 .iter()
437 .find(|role_transition| {
438 role_transition.current_role() == current_role
439 && role_transition.type_() == type_
440 && role_transition.class() == class.id()
441 })
442 .map(|x| x.new_role())
443 }
444
445 #[allow(dead_code)]
446 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
449 self.parsed_policy
450 .role_allowlist()
451 .iter()
452 .find(|role_allow| {
453 role_allow.source_role() == source_role && role_allow.new_role() == new_role
454 })
455 .is_some()
456 }
457
458 fn type_transition_new_type(
459 &self,
460 source_type: TypeId,
461 target_type: TypeId,
462 class: &Class<PS>,
463 ) -> Option<TypeId> {
464 self.parsed_policy
467 .access_vector_rules()
468 .iter()
469 .find(|access_vector_rule| {
470 access_vector_rule.is_type_transition()
471 && access_vector_rule.source_type() == source_type
472 && access_vector_rule.target_type() == target_type
473 && access_vector_rule.target_class() == class.id()
474 })
475 .map(|x| x.new_type().unwrap())
476 }
477
478 fn type_transition_new_type_with_name(
479 &self,
480 source_type: TypeId,
481 target_type: TypeId,
482 class: &Class<PS>,
483 name: NullessByteStr<'_>,
484 ) -> Option<TypeId> {
485 self.parsed_policy.compute_filename_transition(source_type, target_type, class.id(), name)
486 }
487
488 fn range_transition_new_range(
489 &self,
490 source_type: TypeId,
491 target_type: TypeId,
492 class: &Class<PS>,
493 ) -> Option<(SecurityLevel, Option<SecurityLevel>)> {
494 for range_transition in self.parsed_policy.range_transitions() {
495 if range_transition.source_type() == source_type
496 && range_transition.target_type() == target_type
497 && range_transition.target_class() == class.id()
498 {
499 let mls_range = range_transition.mls_range();
500 let low_level = SecurityLevel::new_from_mls_level(mls_range.low());
501 let high_level = mls_range
502 .high()
503 .as_ref()
504 .map(|high_level| SecurityLevel::new_from_mls_level(high_level));
505 return Some((low_level, high_level));
506 }
507 }
508
509 None
510 }
511}
512
513#[derive(Debug)]
526enum PermissionIndex {
527 Class { permission_index: usize },
529 Common { common_symbol_index: usize, permission_index: usize },
532}
533
534fn get_class_index_by_name<'a, PS: ParseStrategy>(
535 classes: &'a Classes<PS>,
536 name: &str,
537) -> Option<usize> {
538 let name_bytes = name.as_bytes();
539 for i in 0..classes.len() {
540 if classes[i].name_bytes() == name_bytes {
541 return Some(i);
542 }
543 }
544
545 None
546}
547
548fn get_common_symbol_index_by_name_bytes<'a, PS: ParseStrategy>(
549 common_symbols: &'a CommonSymbols<PS>,
550 name_bytes: &[u8],
551) -> Option<usize> {
552 for i in 0..common_symbols.len() {
553 if common_symbols[i].name_bytes() == name_bytes {
554 return Some(i);
555 }
556 }
557
558 None
559}
560
561fn get_permission_index_by_name<'a, PS: ParseStrategy>(
562 common_symbols: &'a CommonSymbols<PS>,
563 class: &'a Class<PS>,
564 name: &str,
565) -> Option<PermissionIndex> {
566 if let Some(permission_index) = get_class_permission_index_by_name(class, name) {
567 Some(PermissionIndex::Class { permission_index })
568 } else if let Some(common_symbol_index) =
569 get_common_symbol_index_by_name_bytes(common_symbols, class.common_name_bytes())
570 {
571 let common_symbol = &common_symbols[common_symbol_index];
572 if let Some(permission_index) = get_common_permission_index_by_name(common_symbol, name) {
573 Some(PermissionIndex::Common { common_symbol_index, permission_index })
574 } else {
575 None
576 }
577 } else {
578 None
579 }
580}
581
582fn get_class_permission_index_by_name<'a, PS: ParseStrategy>(
583 class: &'a Class<PS>,
584 name: &str,
585) -> Option<usize> {
586 let name_bytes = name.as_bytes();
587 let permissions = class.permissions();
588 for i in 0..permissions.len() {
589 if permissions[i].name_bytes() == name_bytes {
590 return Some(i);
591 }
592 }
593
594 None
595}
596
597fn get_common_permission_index_by_name<'a, PS: ParseStrategy>(
598 common_symbol: &'a CommonSymbol<PS>,
599 name: &str,
600) -> Option<usize> {
601 let name_bytes = name.as_bytes();
602 let permissions = common_symbol.permissions();
603 for i in 0..permissions.len() {
604 if permissions[i].name_bytes() == name_bytes {
605 return Some(i);
606 }
607 }
608
609 None
610}