1use super::arrays::{ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION, FsContext, FsUseType};
6use super::metadata::HandleUnknown;
7use super::security_context::{SecurityContext, SecurityLevel};
8use super::symbols::{
9 Class, ClassDefault, ClassDefaultRange, Classes, CommonSymbol, CommonSymbols, Permission,
10};
11use super::{ParsedPolicy, RoleId, TypeId};
12use crate::{ClassPermission as _, NullessByteStr, PolicyCap};
13
14use std::collections::HashMap;
15
16pub struct FsUseLabelAndType {
18 pub context: SecurityContext,
19 pub use_type: FsUseType,
20}
21
22#[derive(Debug)]
30pub(super) struct PolicyIndex {
31 classes: HashMap<crate::ObjectClass, usize>,
37 permissions: HashMap<crate::KernelPermission, PermissionIndex>,
40 parsed_policy: ParsedPolicy,
42 cached_object_r_role: RoleId,
44}
45
46impl PolicyIndex {
47 pub fn new(parsed_policy: ParsedPolicy) -> Result<Self, anyhow::Error> {
54 let policy_classes = parsed_policy.classes();
55 let common_symbols = parsed_policy.common_symbols();
56
57 let mut classes = HashMap::with_capacity(policy_classes.len() * 2);
61
62 for known_class in crate::KernelClass::all_variants() {
66 match get_class_index_by_name(policy_classes, known_class.name()) {
67 Some(class_index) => {
68 classes.insert(known_class.into(), class_index);
69 }
70 None => {
71 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
72 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
73 }
74 }
75 }
76 }
77
78 for index in 0..policy_classes.len() {
80 let class = &policy_classes[index];
81 classes.insert(class.id().into(), index);
82 }
83
84 classes.shrink_to_fit();
86
87 let mut permissions =
91 HashMap::with_capacity(crate::KernelPermission::all_variants().count());
92 for known_permission in crate::KernelPermission::all_variants() {
93 let object_class = known_permission.class();
94 if let Some(class_index) = classes.get(&object_class.into()) {
95 let class = &policy_classes[*class_index];
96 if let Some(permission_index) =
97 get_permission_index_by_name(common_symbols, class, known_permission.name())
98 {
99 permissions.insert(known_permission, permission_index);
100 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
101 return Err(anyhow::anyhow!(
102 "missing permission {:?}:{:?}",
103 object_class.name(),
104 known_permission.name(),
105 ));
106 }
107 }
108 }
109 permissions.shrink_to_fit();
110
111 let cached_object_r_role = parsed_policy
113 .role_by_name("object_r".into())
114 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
115 .id();
116
117 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
118
119 for initial_sids in crate::InitialSid::all_variants() {
121 index.resolve_initial_context(*initial_sids);
122 }
123
124 for fs_use in index.parsed_policy.fs_uses() {
126 SecurityContext::new_from_policy_context(fs_use.context());
127 }
128
129 Ok(index)
130 }
131
132 pub fn class<'a>(&'a self, object_class: crate::ObjectClass) -> Option<&'a Class> {
135 let index = self.classes.get(&object_class)?;
136 Some(&self.parsed_policy.classes()[*index])
137 }
138
139 pub fn permission<'a>(
141 &'a self,
142 permission: &crate::KernelPermission,
143 ) -> Option<&'a Permission> {
144 let target_class = self.class(permission.class().into())?;
145 self.permissions.get(permission).map(|p| match p {
146 PermissionIndex::Class { permission_index } => {
147 &target_class.permissions()[*permission_index]
148 }
149 PermissionIndex::Common { common_symbol_index, permission_index } => {
150 let common_symbol = &self.parsed_policy().common_symbols()[*common_symbol_index];
151 &common_symbol.permissions()[*permission_index]
152 }
153 })
154 }
155
156 pub fn compute_create_context_with_name(
164 &self,
165 source: &SecurityContext,
166 target: &SecurityContext,
167 class: crate::ObjectClass,
168 name: NullessByteStr<'_>,
169 ) -> Option<SecurityContext> {
170 let policy_class = self.class(class)?;
171 let type_id = self.type_transition_new_type_with_name(
172 source.type_(),
173 target.type_(),
174 policy_class,
175 name,
176 )?;
177 Some(self.new_security_context_internal(
178 source,
179 target,
180 class,
181 Some(type_id),
183 ))
184 }
185
186 pub fn compute_create_context(
201 &self,
202 source: &SecurityContext,
203 target: &SecurityContext,
204 class: crate::ObjectClass,
205 ) -> SecurityContext {
206 self.new_security_context_internal(source, target, class, None)
207 }
208
209 fn new_security_context_internal(
215 &self,
216 source: &SecurityContext,
217 target: &SecurityContext,
218 target_class: crate::ObjectClass,
219 override_type: Option<TypeId>,
220 ) -> SecurityContext {
221 let Some(policy_class) = self.class(target_class) else {
222 return SecurityContext::new(
227 source.user(),
228 self.cached_object_r_role,
229 target.type_(),
230 source.low_level().clone(),
231 None,
232 );
233 };
234
235 let is_process_or_socket = policy_class.name_bytes() == b"process"
236 || policy_class.common_name_bytes() == b"socket";
237 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
238 if is_process_or_socket {
239 (source.role(), source.type_(), source.low_level(), source.high_level())
240 } else {
241 (self.cached_object_r_role, target.type_(), source.low_level(), None)
242 };
243 let class_defaults = policy_class.defaults();
244
245 let user = match class_defaults.user() {
246 ClassDefault::Source => source.user(),
247 ClassDefault::Target => target.user(),
248 ClassDefault::Unspecified => source.user(),
249 };
250
251 let role = match self.role_transition_new_role(source.role(), target.type_(), policy_class)
252 {
253 Some(new_role) => new_role,
254 None => match class_defaults.role() {
255 ClassDefault::Source => source.role(),
256 ClassDefault::Target => target.role(),
257 ClassDefault::Unspecified => unspecified_role,
258 },
259 };
260
261 let type_ = override_type.unwrap_or_else(|| {
262 match self.parsed_policy.access_vector_rules_find(
263 source.type_(),
264 target.type_(),
265 policy_class.id(),
266 ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION,
267 ) {
268 Some(new_type_rule) => new_type_rule.new_type().unwrap(),
269 None => match class_defaults.type_() {
270 ClassDefault::Source => source.type_(),
271 ClassDefault::Target => target.type_(),
272 ClassDefault::Unspecified => unspecified_type,
273 },
274 }
275 });
276
277 let (low_level, high_level) =
278 match self.range_transition_new_range(source.type_(), target.type_(), policy_class) {
279 Some((low_level, high_level)) => (low_level, high_level),
280 None => match class_defaults.range() {
281 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
282 ClassDefaultRange::SourceHigh => {
283 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
284 }
285 ClassDefaultRange::SourceLowHigh => {
286 (source.low_level().clone(), source.high_level().cloned())
287 }
288 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
289 ClassDefaultRange::TargetHigh => {
290 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
291 }
292 ClassDefaultRange::TargetLowHigh => {
293 (target.low_level().clone(), target.high_level().cloned())
294 }
295 ClassDefaultRange::Unspecified => {
296 (unspecified_low.clone(), unspecified_high.cloned())
297 }
298 },
299 };
300
301 SecurityContext::new(user, role, type_, low_level, high_level)
303 }
304
305 pub(super) fn object_role(&self) -> RoleId {
308 self.cached_object_r_role
309 }
310
311 pub(super) fn parsed_policy(&self) -> &ParsedPolicy {
312 &self.parsed_policy
313 }
314
315 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
318 self.resolve_initial_context(id)
320 }
321
322 pub(super) fn fs_use_label_and_type(
325 &self,
326 fs_type: NullessByteStr<'_>,
327 ) -> Option<FsUseLabelAndType> {
328 self.parsed_policy
329 .fs_uses()
330 .iter()
331 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
332 .map(|fs_use| FsUseLabelAndType {
333 context: SecurityContext::new_from_policy_context(fs_use.context()),
334 use_type: fs_use.behavior(),
335 })
336 }
337
338 pub(super) fn genfscon_label_for_fs_and_path(
343 &self,
344 fs_type: NullessByteStr<'_>,
345 node_path: NullessByteStr<'_>,
346 class: Option<crate::KernelClass>,
347 ) -> Option<SecurityContext> {
348 let node_path = if class == Some(crate::FileClass::Link.into())
349 && !self.parsed_policy.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
350 {
351 "/".into()
355 } else {
356 node_path
357 };
358
359 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
360
361 let fs_contexts = self
363 .parsed_policy
364 .generic_fs_contexts()
365 .iter()
366 .find(|genfscon| genfscon.fs_type() == fs_type.as_bytes())?
367 .contexts();
368
369 let mut result: Option<&FsContext> = None;
382 for fs_context in fs_contexts {
383 if node_path.0.starts_with(fs_context.partial_path()) {
384 if result.is_none()
385 || result.unwrap().partial_path().len() < fs_context.partial_path().len()
386 {
387 if class_id.is_none()
388 || fs_context
389 .class()
390 .map(|other| other == class_id.unwrap())
391 .unwrap_or(true)
392 {
393 result = Some(fs_context);
394 }
395 }
396 }
397 }
398
399 result.and_then(|fs_context| {
402 Some(SecurityContext::new_from_policy_context(fs_context.context()))
403 })
404 }
405
406 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
408 SecurityContext::new_from_policy_context(self.parsed_policy().initial_context(id))
409 }
410
411 fn role_transition_new_role(
412 &self,
413 current_role: RoleId,
414 type_: TypeId,
415 class: &Class,
416 ) -> Option<RoleId> {
417 self.parsed_policy
418 .role_transitions()
419 .iter()
420 .find(|role_transition| {
421 role_transition.current_role() == current_role
422 && role_transition.type_() == type_
423 && role_transition.class() == class.id()
424 })
425 .map(|x| x.new_role())
426 }
427
428 #[allow(dead_code)]
429 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
432 self.parsed_policy
433 .role_allowlist()
434 .iter()
435 .find(|role_allow| {
436 role_allow.source_role() == source_role && role_allow.new_role() == new_role
437 })
438 .is_some()
439 }
440
441 fn type_transition_new_type_with_name(
442 &self,
443 source_type: TypeId,
444 target_type: TypeId,
445 class: &Class,
446 name: NullessByteStr<'_>,
447 ) -> Option<TypeId> {
448 self.parsed_policy.compute_filename_transition(source_type, target_type, class.id(), name)
449 }
450
451 fn range_transition_new_range(
452 &self,
453 source_type: TypeId,
454 target_type: TypeId,
455 class: &Class,
456 ) -> Option<(SecurityLevel, Option<SecurityLevel>)> {
457 for range_transition in self.parsed_policy.range_transitions() {
458 if range_transition.source_type() == source_type
459 && range_transition.target_type() == target_type
460 && range_transition.target_class() == class.id()
461 {
462 let mls_range = range_transition.mls_range();
463 let low_level = SecurityLevel::new_from_mls_level(mls_range.low());
464 let high_level = mls_range
465 .high()
466 .as_ref()
467 .map(|high_level| SecurityLevel::new_from_mls_level(high_level));
468 return Some((low_level, high_level));
469 }
470 }
471
472 None
473 }
474}
475
476#[derive(Debug)]
489enum PermissionIndex {
490 Class { permission_index: usize },
492 Common { common_symbol_index: usize, permission_index: usize },
495}
496
497fn get_class_index_by_name<'a>(classes: &'a Classes, name: &str) -> Option<usize> {
498 let name_bytes = name.as_bytes();
499 classes.iter().position(|class| class.name_bytes() == name_bytes)
500}
501
502fn get_common_symbol_index_by_name_bytes<'a>(
503 common_symbols: &'a CommonSymbols,
504 name_bytes: &[u8],
505) -> Option<usize> {
506 common_symbols.iter().position(|common_symbol| common_symbol.name_bytes() == name_bytes)
507}
508
509fn get_permission_index_by_name<'a>(
510 common_symbols: &'a CommonSymbols,
511 class: &'a Class,
512 name: &str,
513) -> Option<PermissionIndex> {
514 if let Some(permission_index) = get_class_permission_index_by_name(class, name) {
515 Some(PermissionIndex::Class { permission_index })
516 } else if let Some(common_symbol_index) =
517 get_common_symbol_index_by_name_bytes(common_symbols, class.common_name_bytes())
518 {
519 let common_symbol = &common_symbols[common_symbol_index];
520 if let Some(permission_index) = get_common_permission_index_by_name(common_symbol, name) {
521 Some(PermissionIndex::Common { common_symbol_index, permission_index })
522 } else {
523 None
524 }
525 } else {
526 None
527 }
528}
529
530fn get_class_permission_index_by_name<'a>(class: &'a Class, name: &str) -> Option<usize> {
531 let name_bytes = name.as_bytes();
532 class.permissions().iter().position(|permission| permission.name_bytes() == name_bytes)
533}
534
535fn get_common_permission_index_by_name<'a>(
536 common_symbol: &'a CommonSymbol,
537 name: &str,
538) -> Option<usize> {
539 let name_bytes = name.as_bytes();
540 common_symbol.permissions().iter().position(|permission| permission.name_bytes() == name_bytes)
541}