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 _, 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>,
38 permissions: HashMap<crate::KernelPermission, PermissionIndex>,
41 parsed_policy: ParsedPolicy<PS>,
43 cached_object_r_role: RoleId,
45}
46
47impl<PS: ParseStrategy> PolicyIndex<PS> {
48 pub fn new(parsed_policy: ParsedPolicy<PS>) -> Result<Self, anyhow::Error> {
55 let policy_classes = parsed_policy.classes();
56 let common_symbols = parsed_policy.common_symbols();
57
58 let mut classes = HashMap::with_capacity(policy_classes.len() * 2);
62
63 for known_class in crate::KernelClass::all_variants() {
67 match get_class_index_by_name(policy_classes, known_class.name()) {
68 Some(class_index) => {
69 classes.insert(known_class.into(), class_index);
70 }
71 None => {
72 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
73 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
74 }
75 }
76 }
77 }
78
79 for index in 0..policy_classes.len() {
81 let class = &policy_classes[index];
82 classes.insert(class.id().into(), index);
83 }
84
85 classes.shrink_to_fit();
87
88 let mut permissions =
92 HashMap::with_capacity(crate::KernelPermission::all_variants().count());
93 for known_permission in crate::KernelPermission::all_variants() {
94 let object_class = known_permission.class();
95 if let Some(class_index) = classes.get(&object_class.into()) {
96 let class = &policy_classes[*class_index];
97 if let Some(permission_index) =
98 get_permission_index_by_name(common_symbols, class, known_permission.name())
99 {
100 permissions.insert(known_permission, permission_index);
101 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
102 return Err(anyhow::anyhow!(
103 "missing permission {:?}:{:?}",
104 object_class.name(),
105 known_permission.name(),
106 ));
107 }
108 }
109 }
110 permissions.shrink_to_fit();
111
112 let cached_object_r_role = parsed_policy
114 .role_by_name("object_r".into())
115 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
116 .id();
117
118 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
119
120 for initial_sids in crate::InitialSid::all_variants() {
122 index.resolve_initial_context(*initial_sids);
123 }
124
125 for fs_use in index.parsed_policy.fs_uses() {
127 SecurityContext::new_from_policy_context(fs_use.context());
128 }
129
130 Ok(index)
131 }
132
133 pub fn class<'a>(&'a self, object_class: crate::ObjectClass) -> Option<&'a Class<PS>> {
136 let index = self.classes.get(&object_class)?;
137 Some(&self.parsed_policy.classes()[*index])
138 }
139
140 pub fn permission<'a>(
142 &'a self,
143 permission: &crate::KernelPermission,
144 ) -> Option<&'a Permission<PS>> {
145 let target_class = self.class(permission.class().into())?;
146 self.permissions.get(permission).map(|p| match p {
147 PermissionIndex::Class { permission_index } => {
148 &target_class.permissions()[*permission_index]
149 }
150 PermissionIndex::Common { common_symbol_index, permission_index } => {
151 let common_symbol = &self.parsed_policy().common_symbols()[*common_symbol_index];
152 &common_symbol.permissions()[*permission_index]
153 }
154 })
155 }
156
157 pub fn compute_create_context_with_name(
165 &self,
166 source: &SecurityContext,
167 target: &SecurityContext,
168 class: crate::ObjectClass,
169 name: NullessByteStr<'_>,
170 ) -> Option<SecurityContext> {
171 let policy_class = self.class(class)?;
172 let type_id = self.type_transition_new_type_with_name(
173 source.type_(),
174 target.type_(),
175 policy_class,
176 name,
177 )?;
178 Some(self.new_security_context_internal(
179 source,
180 target,
181 class,
182 Some(type_id),
184 ))
185 }
186
187 pub fn compute_create_context(
202 &self,
203 source: &SecurityContext,
204 target: &SecurityContext,
205 class: crate::ObjectClass,
206 ) -> SecurityContext {
207 self.new_security_context_internal(source, target, class, None)
208 }
209
210 fn new_security_context_internal(
216 &self,
217 source: &SecurityContext,
218 target: &SecurityContext,
219 target_class: crate::ObjectClass,
220 override_type: Option<TypeId>,
221 ) -> SecurityContext {
222 let Some(policy_class) = self.class(target_class) else {
223 return SecurityContext::new(
228 source.user(),
229 self.cached_object_r_role,
230 target.type_(),
231 source.low_level().clone(),
232 None,
233 );
234 };
235
236 let is_process_or_socket = policy_class.name_bytes() == b"process"
237 || policy_class.common_name_bytes() == b"socket";
238 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
239 if is_process_or_socket {
240 (source.role(), source.type_(), source.low_level(), source.high_level())
241 } else {
242 (self.cached_object_r_role, target.type_(), source.low_level(), None)
243 };
244 let class_defaults = policy_class.defaults();
245
246 let user = match class_defaults.user() {
247 ClassDefault::Source => source.user(),
248 ClassDefault::Target => target.user(),
249 ClassDefault::Unspecified => source.user(),
250 };
251
252 let role = match self.role_transition_new_role(source.role(), target.type_(), policy_class)
253 {
254 Some(new_role) => new_role,
255 None => match class_defaults.role() {
256 ClassDefault::Source => source.role(),
257 ClassDefault::Target => target.role(),
258 ClassDefault::Unspecified => unspecified_role,
259 },
260 };
261
262 let type_ = override_type.unwrap_or_else(|| {
263 match self.type_transition_new_type(source.type_(), target.type_(), policy_class) {
264 Some(new_type) => new_type,
265 None => match class_defaults.type_() {
266 ClassDefault::Source => source.type_(),
267 ClassDefault::Target => target.type_(),
268 ClassDefault::Unspecified => unspecified_type,
269 },
270 }
271 });
272
273 let (low_level, high_level) =
274 match self.range_transition_new_range(source.type_(), target.type_(), policy_class) {
275 Some((low_level, high_level)) => (low_level, high_level),
276 None => match class_defaults.range() {
277 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
278 ClassDefaultRange::SourceHigh => {
279 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
280 }
281 ClassDefaultRange::SourceLowHigh => {
282 (source.low_level().clone(), source.high_level().cloned())
283 }
284 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
285 ClassDefaultRange::TargetHigh => {
286 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
287 }
288 ClassDefaultRange::TargetLowHigh => {
289 (target.low_level().clone(), target.high_level().cloned())
290 }
291 ClassDefaultRange::Unspecified => {
292 (unspecified_low.clone(), unspecified_high.cloned())
293 }
294 },
295 };
296
297 SecurityContext::new(user, role, type_, low_level, high_level)
299 }
300
301 pub(super) fn object_role(&self) -> RoleId {
304 self.cached_object_r_role
305 }
306
307 pub(super) fn parsed_policy(&self) -> &ParsedPolicy<PS> {
308 &self.parsed_policy
309 }
310
311 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
314 self.resolve_initial_context(id)
316 }
317
318 pub(super) fn fs_use_label_and_type(
321 &self,
322 fs_type: NullessByteStr<'_>,
323 ) -> Option<FsUseLabelAndType> {
324 self.parsed_policy
325 .fs_uses()
326 .iter()
327 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
328 .map(|fs_use| FsUseLabelAndType {
329 context: SecurityContext::new_from_policy_context(fs_use.context()),
330 use_type: fs_use.behavior(),
331 })
332 }
333
334 pub(super) fn genfscon_label_for_fs_and_path(
339 &self,
340 fs_type: NullessByteStr<'_>,
341 node_path: NullessByteStr<'_>,
342 class_id: Option<ClassId>,
343 ) -> Option<SecurityContext> {
344 let fs_contexts = self
346 .parsed_policy
347 .generic_fs_contexts()
348 .iter()
349 .find(|genfscon| genfscon.fs_type() == fs_type.as_bytes())?
350 .contexts();
351
352 let mut result: Option<&FsContext<PS>> = None;
365 for fs_context in fs_contexts {
366 if node_path.0.starts_with(fs_context.partial_path()) {
367 if result.is_none()
368 || result.unwrap().partial_path().len() < fs_context.partial_path().len()
369 {
370 if class_id.is_none()
371 || fs_context
372 .class()
373 .map(|other| other == class_id.unwrap())
374 .unwrap_or(true)
375 {
376 result = Some(fs_context);
377 }
378 }
379 }
380 }
381
382 result.and_then(|fs_context| {
385 Some(SecurityContext::new_from_policy_context(fs_context.context()))
386 })
387 }
388
389 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
391 SecurityContext::new_from_policy_context(self.parsed_policy().initial_context(id))
392 }
393
394 fn role_transition_new_role(
395 &self,
396 current_role: RoleId,
397 type_: TypeId,
398 class: &Class<PS>,
399 ) -> Option<RoleId> {
400 self.parsed_policy
401 .role_transitions()
402 .iter()
403 .find(|role_transition| {
404 role_transition.current_role() == current_role
405 && role_transition.type_() == type_
406 && role_transition.class() == class.id()
407 })
408 .map(|x| x.new_role())
409 }
410
411 #[allow(dead_code)]
412 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
415 self.parsed_policy
416 .role_allowlist()
417 .iter()
418 .find(|role_allow| {
419 role_allow.source_role() == source_role && role_allow.new_role() == new_role
420 })
421 .is_some()
422 }
423
424 fn type_transition_new_type(
425 &self,
426 source_type: TypeId,
427 target_type: TypeId,
428 class: &Class<PS>,
429 ) -> Option<TypeId> {
430 self.parsed_policy
433 .access_vector_rules()
434 .iter()
435 .find(|access_vector_rule| {
436 access_vector_rule.is_type_transition()
437 && access_vector_rule.source_type() == source_type
438 && access_vector_rule.target_type() == target_type
439 && access_vector_rule.target_class() == class.id()
440 })
441 .map(|x| x.new_type().unwrap())
442 }
443
444 fn type_transition_new_type_with_name(
445 &self,
446 source_type: TypeId,
447 target_type: TypeId,
448 class: &Class<PS>,
449 name: NullessByteStr<'_>,
450 ) -> Option<TypeId> {
451 self.parsed_policy.compute_filename_transition(source_type, target_type, class.id(), name)
452 }
453
454 fn range_transition_new_range(
455 &self,
456 source_type: TypeId,
457 target_type: TypeId,
458 class: &Class<PS>,
459 ) -> Option<(SecurityLevel, Option<SecurityLevel>)> {
460 for range_transition in self.parsed_policy.range_transitions() {
461 if range_transition.source_type() == source_type
462 && range_transition.target_type() == target_type
463 && range_transition.target_class() == class.id()
464 {
465 let mls_range = range_transition.mls_range();
466 let low_level = SecurityLevel::new_from_mls_level(mls_range.low());
467 let high_level = mls_range
468 .high()
469 .as_ref()
470 .map(|high_level| SecurityLevel::new_from_mls_level(high_level));
471 return Some((low_level, high_level));
472 }
473 }
474
475 None
476 }
477}
478
479#[derive(Debug)]
492enum PermissionIndex {
493 Class { permission_index: usize },
495 Common { common_symbol_index: usize, permission_index: usize },
498}
499
500fn get_class_index_by_name<'a, PS: ParseStrategy>(
501 classes: &'a Classes<PS>,
502 name: &str,
503) -> Option<usize> {
504 let name_bytes = name.as_bytes();
505 for i in 0..classes.len() {
506 if classes[i].name_bytes() == name_bytes {
507 return Some(i);
508 }
509 }
510
511 None
512}
513
514fn get_common_symbol_index_by_name_bytes<'a, PS: ParseStrategy>(
515 common_symbols: &'a CommonSymbols<PS>,
516 name_bytes: &[u8],
517) -> Option<usize> {
518 for i in 0..common_symbols.len() {
519 if common_symbols[i].name_bytes() == name_bytes {
520 return Some(i);
521 }
522 }
523
524 None
525}
526
527fn get_permission_index_by_name<'a, PS: ParseStrategy>(
528 common_symbols: &'a CommonSymbols<PS>,
529 class: &'a Class<PS>,
530 name: &str,
531) -> Option<PermissionIndex> {
532 if let Some(permission_index) = get_class_permission_index_by_name(class, name) {
533 Some(PermissionIndex::Class { permission_index })
534 } else if let Some(common_symbol_index) =
535 get_common_symbol_index_by_name_bytes(common_symbols, class.common_name_bytes())
536 {
537 let common_symbol = &common_symbols[common_symbol_index];
538 if let Some(permission_index) = get_common_permission_index_by_name(common_symbol, name) {
539 Some(PermissionIndex::Common { common_symbol_index, permission_index })
540 } else {
541 None
542 }
543 } else {
544 None
545 }
546}
547
548fn get_class_permission_index_by_name<'a, PS: ParseStrategy>(
549 class: &'a Class<PS>,
550 name: &str,
551) -> Option<usize> {
552 let name_bytes = name.as_bytes();
553 let permissions = class.permissions();
554 for i in 0..permissions.len() {
555 if permissions[i].name_bytes() == name_bytes {
556 return Some(i);
557 }
558 }
559
560 None
561}
562
563fn get_common_permission_index_by_name<'a, PS: ParseStrategy>(
564 common_symbol: &'a CommonSymbol<PS>,
565 name: &str,
566) -> Option<usize> {
567 let name_bytes = name.as_bytes();
568 let permissions = common_symbol.permissions();
569 for i in 0..permissions.len() {
570 if permissions[i].name_bytes() == name_bytes {
571 return Some(i);
572 }
573 }
574
575 None
576}