1use super::arrays::{FsContext, FsUseType};
6use super::security_context::SecurityContext;
7use super::{
8 AccessDecision, AccessVector, ClassId, MlsLevel, ParsedPolicy, PermissionId, RoleId, TypeId,
9};
10use crate::new_policy::rules::{HasRuleKey, RuleKind};
11use crate::new_policy::traits::{HasName, HasPolicyId};
12use crate::new_policy::{
13 Class, ClassDefault, ClassDefaultRange, CommonSymbol, HandleUnknown, IdAndNameIndexed,
14 SymbolArray,
15};
16use crate::{
17 ClassPermission as _, KernelClass, KernelPermission, NullessByteStr, PolicyCap,
18 ProcessPermission,
19};
20
21use std::collections::HashMap;
22use std::ops::Deref;
23
24use strum::VariantArray as _;
25
26pub struct FsUseLabelAndType {
28 pub context: SecurityContext,
29 pub use_type: FsUseType,
30}
31
32type KernelPermissionIdsArray = [Option<PermissionId>; 32];
34
35#[derive(Debug)]
43pub struct PolicyIndex {
44 classes: HashMap<KernelClass, ClassId>,
47 permissions: [KernelPermissionIdsArray; KernelClass::VARIANTS.len()],
49 parsed_policy: ParsedPolicy,
51 cached_object_r_role: RoleId,
53 cached_process_class: Option<ClassId>,
55}
56
57impl PolicyIndex {
58 pub fn new(parsed_policy: ParsedPolicy) -> Result<Self, anyhow::Error> {
65 let policy_classes = parsed_policy.classes();
66 let common_symbols = parsed_policy.common_symbols();
67
68 let mut classes = HashMap::with_capacity(crate::KernelClass::VARIANTS.len());
69
70 for known_class in crate::KernelClass::VARIANTS {
74 match policy_classes.get_by_name(known_class.name().as_bytes()) {
75 Some(class) => {
76 classes.insert(*known_class, class.id());
77 }
78 None => {
79 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
80 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
81 }
82 }
83 }
84 }
85
86 classes.shrink_to_fit();
88
89 let mut permissions = [KernelPermissionIdsArray::default(); _];
93 for kernel_permission in crate::KernelPermission::all_variants() {
94 let kernel_class_name = kernel_permission.class().name();
95 if let Some(class) = policy_classes.get_by_name(kernel_class_name.as_bytes()) {
96 if let Some(permission_id) =
97 get_permission_id_by_name(common_symbols, class, kernel_permission.name())
98 {
99 let kernel_class_id = kernel_permission.class() as usize;
100 let kernel_permission_id = kernel_permission.id() as usize;
101 permissions[kernel_class_id][kernel_permission_id] = Some(permission_id);
102 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
103 return Err(anyhow::anyhow!(
104 "missing permission {:?}:{:?}",
105 kernel_class_name,
106 kernel_permission.name(),
107 ));
108 }
109 }
110 }
111
112 let cached_object_r_role = parsed_policy
114 .roles()
115 .get_by_name(b"object_r")
116 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
117 .id();
118
119 let cached_process_class = classes.get(&KernelClass::Process).copied();
120
121 let index = Self {
122 classes,
123 permissions,
124 parsed_policy,
125 cached_object_r_role,
126 cached_process_class,
127 };
128
129 for initial_sids in crate::InitialSid::all_variants() {
131 index.resolve_initial_context(*initial_sids);
132 }
133
134 for fs_use in index.parsed_policy.fs_uses() {
136 SecurityContext::new_from_policy_context(fs_use.context());
137 }
138
139 Ok(index)
140 }
141
142 pub(super) fn class(&self, object_class: crate::ObjectClass) -> Option<&Class> {
145 match object_class {
146 crate::ObjectClass::Kernel(kernel_class) => {
147 let &class_id = self.classes.get(&kernel_class)?;
148 self.classes().get_by_id(class_id)
149 }
150 crate::ObjectClass::ClassId(class_id) => self.classes().get_by_id(class_id),
151 }
152 }
153
154 pub fn kernel_permission_to_access_vector<P: Into<KernelPermission>>(
156 &self,
157 permission: P,
158 ) -> Option<AccessVector> {
159 let permission = permission.into();
160 let class_index = permission.class() as usize;
161 let permission_index = permission.id() as usize;
162 let permission_id = self.permissions[class_index][permission_index]?;
163 Some(permission_id.into())
164 }
165
166 pub fn compute_create_context_with_name(
174 &self,
175 source: &SecurityContext,
176 target: &SecurityContext,
177 class: crate::ObjectClass,
178 name: NullessByteStr<'_>,
179 ) -> Option<SecurityContext> {
180 let policy_class = self.class(class)?;
181 let type_id = self.type_transition_new_type_with_name(
182 source.type_(),
183 target.type_(),
184 &policy_class,
185 name,
186 )?;
187 Some(self.new_security_context_internal(
188 source,
189 target,
190 class,
191 Some(type_id),
193 ))
194 }
195
196 pub fn compute_create_context(
211 &self,
212 source: &SecurityContext,
213 target: &SecurityContext,
214 class: crate::ObjectClass,
215 ) -> SecurityContext {
216 self.new_security_context_internal(source, target, class, None)
217 }
218
219 fn new_security_context_internal(
225 &self,
226 source: &SecurityContext,
227 target: &SecurityContext,
228 target_class: crate::ObjectClass,
229 override_type: Option<TypeId>,
230 ) -> SecurityContext {
231 let Some(policy_class) = self.class(target_class) else {
232 return SecurityContext::new(
237 source.user(),
238 self.cached_object_r_role,
239 target.type_(),
240 source.low_level().clone(),
241 None,
242 );
243 };
244
245 let is_process_or_socket =
246 policy_class.name() == b"process" || policy_class.common_name() == b"socket";
247 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
248 if is_process_or_socket {
249 (source.role(), source.type_(), source.low_level(), source.high_level())
250 } else {
251 (self.cached_object_r_role, target.type_(), source.low_level(), None)
252 };
253 let class_defaults = policy_class.defaults();
254
255 let user = match class_defaults.user() {
256 ClassDefault::Source => source.user(),
257 ClassDefault::Target => target.user(),
258 ClassDefault::Unspecified => source.user(),
259 };
260
261 let role = match self.role_transition_new_role(source.role(), target.type_(), &policy_class)
262 {
263 Some(new_role) => new_role,
264 None => match class_defaults.role() {
265 ClassDefault::Source => source.role(),
266 ClassDefault::Target => target.role(),
267 ClassDefault::Unspecified => unspecified_role,
268 },
269 };
270
271 let type_ = override_type.unwrap_or_else(|| {
272 let transition = self
273 .parsed_policy
274 .access_vector_rules()
275 .find_type_rules(source.type_(), target.type_(), policy_class.id())
276 .find(|rule| rule.kind() == RuleKind::TypeTransition)
277 .map(|rule| rule.new_type());
278 match transition {
279 Some(new_type) => new_type,
280 None => match class_defaults.type_() {
281 ClassDefault::Source => source.type_(),
282 ClassDefault::Target => target.type_(),
283 ClassDefault::Unspecified => unspecified_type,
284 },
285 }
286 });
287
288 let (low_level, high_level) =
289 match self.range_transition_new_range(source.type_(), target.type_(), &policy_class) {
290 Some((low_level, high_level)) => (low_level, high_level),
291 None => match class_defaults.range() {
292 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
293 ClassDefaultRange::SourceHigh => {
294 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
295 }
296 ClassDefaultRange::SourceLowHigh => {
297 (source.low_level().clone(), source.high_level().cloned())
298 }
299 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
300 ClassDefaultRange::TargetHigh => {
301 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
302 }
303 ClassDefaultRange::TargetLowHigh => {
304 (target.low_level().clone(), target.high_level().cloned())
305 }
306 ClassDefaultRange::Unspecified => {
307 (unspecified_low.clone(), unspecified_high.cloned())
308 }
309 ClassDefaultRange::UnknownUsedValue => {
310 unreachable!("Invalid ClassDefaultRange in validated policy")
311 }
312 },
313 };
314
315 SecurityContext::new(user, role, type_, low_level, high_level)
317 }
318
319 pub(super) fn compute_access_decision(
322 &self,
323 source_context: &SecurityContext,
324 target_context: &SecurityContext,
325 target_class: &Class,
326 ) -> AccessDecision {
327 let mut access_decision = self.parsed_policy.compute_access_decision(
328 source_context,
329 target_context,
330 target_class,
331 );
332
333 if source_context.role() != target_context.role()
336 && Some(target_class.id()) == self.cached_process_class
337 {
338 let process_trans_perms = self.process_trans_perms();
339 if (access_decision.allow & process_trans_perms) != AccessVector::NONE
340 && !self.role_transition_is_explicitly_allowed(
341 source_context.role(),
342 target_context.role(),
343 )
344 {
345 access_decision.allow -= process_trans_perms;
349 }
350 }
351
352 access_decision
353 }
354
355 fn process_trans_perms(&self) -> AccessVector {
357 let mut perms = self
358 .kernel_permission_to_access_vector(ProcessPermission::Transition)
359 .unwrap_or(AccessVector::NONE);
360 perms |= self
361 .kernel_permission_to_access_vector(ProcessPermission::DynTransition)
362 .unwrap_or(AccessVector::NONE);
363 perms
364 }
365
366 pub(super) fn object_role(&self) -> RoleId {
369 self.cached_object_r_role
370 }
371
372 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
375 self.resolve_initial_context(id)
377 }
378
379 pub(super) fn fs_use_label_and_type(
382 &self,
383 fs_type: NullessByteStr<'_>,
384 ) -> Option<FsUseLabelAndType> {
385 self.parsed_policy
386 .fs_uses()
387 .iter()
388 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
389 .map(|fs_use| FsUseLabelAndType {
390 context: SecurityContext::new_from_policy_context(fs_use.context()),
391 use_type: fs_use.behavior(),
392 })
393 }
394
395 pub(super) fn genfscon_label_for_fs_and_path(
400 &self,
401 fs_type: NullessByteStr<'_>,
402 node_path: NullessByteStr<'_>,
403 class: Option<crate::KernelClass>,
404 ) -> Option<SecurityContext> {
405 let node_path = if class == Some(crate::FileClass::LnkFile.into())
406 && !self.parsed_policy.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
407 {
408 "/".into()
412 } else {
413 node_path
414 };
415
416 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
417
418 let fs_contexts = self
420 .parsed_policy
421 .genfscon_find_all(std::str::from_utf8(fs_type.as_bytes()).expect("fs type is valid"));
422
423 #[derive(PartialEq)]
424 enum OrderType {
425 Alphabetic,
426 ByLength,
427 Unknown,
428 }
429 let mut result: Option<FsContext> = None;
442 let mut order_type = OrderType::Unknown;
443 let mut prev_path_bytes: Option<Vec<u8>> = None;
444 for fs_context in fs_contexts {
445 let path = fs_context.partial_path();
447 if order_type == OrderType::Unknown {
448 if let Some(prev) = &prev_path_bytes {
449 if path.len() > prev.len() {
450 order_type = OrderType::Alphabetic;
451 } else if path < prev.as_slice() {
452 order_type = OrderType::ByLength;
453 }
454 }
455 prev_path_bytes = Some(path.to_vec());
456 }
457
458 let class_matches = class_id.is_none()
460 || fs_context
461 .class()
462 .map(|other| other == class_id.unwrap().into())
463 .unwrap_or(true);
464 if !class_matches {
465 continue;
466 }
467
468 if order_type == OrderType::Alphabetic && fs_context.partial_path() > node_path.0 {
469 break;
474 }
475
476 if node_path.0.starts_with(fs_context.partial_path()) {
477 if result
478 .as_ref()
479 .map_or(true, |c| c.partial_path().len() < fs_context.partial_path().len())
480 {
481 result = Some(fs_context);
483 if order_type == OrderType::ByLength {
484 break;
485 }
486 }
487 }
488 }
489
490 result.and_then(|fs_context| {
493 Some(SecurityContext::new_from_policy_context(fs_context.context()))
494 })
495 }
496
497 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
499 SecurityContext::new_from_policy_context(self.parsed_policy.initial_context(id))
500 }
501
502 fn role_transition_new_role(
503 &self,
504 current_role: RoleId,
505 type_: TypeId,
506 class: &Class,
507 ) -> Option<RoleId> {
508 self.parsed_policy
509 .role_transitions()
510 .iter()
511 .find(|role_transition| {
512 role_transition.current_role() == current_role
513 && role_transition.type_() == type_
514 && role_transition.class() == class.id().into()
515 })
516 .map(|x| x.new_role())
517 }
518
519 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
520 self.parsed_policy.role_allowlist().iter().any(|role_allow| {
521 role_allow.source_role() == source_role && role_allow.new_role() == new_role
522 })
523 }
524
525 fn type_transition_new_type_with_name(
526 &self,
527 source_type: TypeId,
528 target_type: TypeId,
529 class: &Class,
530 name: NullessByteStr<'_>,
531 ) -> Option<TypeId> {
532 self.parsed_policy.compute_filename_transition(
533 source_type,
534 target_type,
535 class.id().into(),
536 name,
537 )
538 }
539
540 fn range_transition_new_range(
541 &self,
542 source_type: TypeId,
543 target_type: TypeId,
544 class: &Class,
545 ) -> Option<(MlsLevel, Option<MlsLevel>)> {
546 for range_transition in self.parsed_policy.range_transitions() {
547 if range_transition.source_type() == source_type
548 && range_transition.target_type() == target_type
549 && range_transition.target_class() == class.id().into()
550 {
551 let mls_range = range_transition.mls_range();
552 let low_level = mls_range.low().clone();
553 let high_level = mls_range.high().clone();
554 return Some((low_level, high_level));
555 }
556 }
557
558 None
559 }
560}
561
562fn get_permission_id_by_name(
565 common_symbols: &IdAndNameIndexed<SymbolArray<CommonSymbol>>,
566 class: &Class,
567 name: &str,
568) -> Option<PermissionId> {
569 let name = name.as_bytes();
570 if let Some(permission) = class.permissions().iter().find(|p| p.name() == name) {
571 return Some(permission.id());
572 }
573 let common_name = class.common_name();
574 if !common_name.is_empty() {
575 let common_symbol = common_symbols.get_by_name(common_name)?;
576 let permission = common_symbol.permissions().iter().find(|p| p.name() == name)?;
577 return Some(permission.id());
578 }
579 None
580}
581
582impl Deref for PolicyIndex {
583 type Target = ParsedPolicy;
584
585 fn deref(&self) -> &Self::Target {
586 &self.parsed_policy
587 }
588}