1use super::security_context::SecurityContext;
6use super::{
7 AccessDecision, AccessVector, ClassId, MlsLevel, ParsedPolicy, PermissionId, RoleId, TypeId,
8};
9use crate::new_policy::rules::{HasRuleKey, RuleKind};
10use crate::new_policy::traits::{HasName, HasPolicyId};
11use crate::new_policy::{
12 CategorySet, Class, ClassDefault, ClassDefaultRange, CommonSymbol, FsUseType, GenfsConPath,
13 HandleUnknown, IdAndNameIndexed, SymbolArray,
14};
15use crate::{
16 ClassPermission as _, KernelClass, KernelPermission, NullessByteStr, PolicyCap,
17 ProcessPermission,
18};
19
20use std::collections::HashMap;
21use std::ops::Deref;
22
23use strum::VariantArray as _;
24
25pub struct FsUseLabelAndType {
27 pub context: SecurityContext,
28 pub use_type: FsUseType,
29}
30
31type KernelPermissionIdsArray = [Option<PermissionId>; 32];
33
34#[derive(Debug)]
42pub struct PolicyIndex {
43 classes: HashMap<KernelClass, ClassId>,
46 permissions: [KernelPermissionIdsArray; KernelClass::VARIANTS.len()],
48 parsed_policy: ParsedPolicy,
50 cached_object_r_role: RoleId,
52 cached_process_class: Option<ClassId>,
54}
55
56impl PolicyIndex {
57 pub fn new(parsed_policy: ParsedPolicy) -> Result<Self, anyhow::Error> {
64 let policy_classes = parsed_policy.classes();
65 let common_symbols = parsed_policy.common_symbols();
66
67 let mut classes = HashMap::with_capacity(crate::KernelClass::VARIANTS.len());
68
69 for known_class in crate::KernelClass::VARIANTS {
73 match policy_classes.get_by_name(known_class.name().as_bytes()) {
74 Some(class) => {
75 classes.insert(*known_class, class.id());
76 }
77 None => {
78 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
79 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
80 }
81 }
82 }
83 }
84
85 classes.shrink_to_fit();
87
88 let mut permissions = [KernelPermissionIdsArray::default(); _];
92 for kernel_permission in crate::KernelPermission::all_variants() {
93 let kernel_class_name = kernel_permission.class().name();
94 if let Some(class) = policy_classes.get_by_name(kernel_class_name.as_bytes()) {
95 if let Some(permission_id) =
96 get_permission_id_by_name(common_symbols, class, kernel_permission.name())
97 {
98 let kernel_class_id = kernel_permission.class() as usize;
99 let kernel_permission_id = kernel_permission.id() as usize;
100 permissions[kernel_class_id][kernel_permission_id] = Some(permission_id);
101 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
102 return Err(anyhow::anyhow!(
103 "missing permission {:?}:{:?}",
104 kernel_class_name,
105 kernel_permission.name(),
106 ));
107 }
108 }
109 }
110
111 let cached_object_r_role = parsed_policy
113 .roles()
114 .get_by_name(b"object_r")
115 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
116 .id();
117
118 let cached_process_class = classes.get(&KernelClass::Process).copied();
119
120 let index = Self {
121 classes,
122 permissions,
123 parsed_policy,
124 cached_object_r_role,
125 cached_process_class,
126 };
127
128 Ok(index)
129 }
130
131 pub(super) fn class(&self, object_class: crate::ObjectClass) -> Option<&Class> {
134 match object_class {
135 crate::ObjectClass::Kernel(kernel_class) => {
136 let &class_id = self.classes.get(&kernel_class)?;
137 self.classes().get_by_id(class_id)
138 }
139 crate::ObjectClass::ClassId(class_id) => self.classes().get_by_id(class_id),
140 }
141 }
142
143 pub fn kernel_permission_to_access_vector<P: Into<KernelPermission>>(
145 &self,
146 permission: P,
147 ) -> Option<AccessVector> {
148 let permission = permission.into();
149 let class_index = permission.class() as usize;
150 let permission_index = permission.id() as usize;
151 let permission_id = self.permissions[class_index][permission_index]?;
152 Some(permission_id.into())
153 }
154
155 pub fn compute_create_context(
172 &self,
173 source: &SecurityContext,
174 target: &SecurityContext,
175 class: crate::ObjectClass,
176 name: &[u8],
177 ) -> SecurityContext {
178 let override_type = if !name.is_empty() {
179 self.class(class).and_then(|policy_class| {
180 self.type_transition_new_type_with_name(
181 source.type_(),
182 target.type_(),
183 &policy_class,
184 name,
185 )
186 })
187 } else {
188 None
189 };
190 self.new_security_context_internal(source, target, class, override_type)
191 }
192
193 fn new_security_context_internal(
198 &self,
199 source: &SecurityContext,
200 target: &SecurityContext,
201 target_class: crate::ObjectClass,
202 override_type: Option<TypeId>,
203 ) -> SecurityContext {
204 let Some(policy_class) = self.class(target_class) else {
205 return SecurityContext::new(
210 source.user(),
211 self.cached_object_r_role,
212 target.type_(),
213 source.low_level().clone(),
214 None,
215 );
216 };
217
218 let is_process_or_socket =
219 policy_class.name() == b"process" || policy_class.common_name() == b"socket";
220 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
221 if is_process_or_socket {
222 (source.role(), source.type_(), source.low_level(), source.high_level())
223 } else {
224 (self.cached_object_r_role, target.type_(), source.low_level(), None)
225 };
226 let class_defaults = policy_class.defaults();
227
228 let user = match class_defaults.user() {
229 ClassDefault::Source => source.user(),
230 ClassDefault::Target => target.user(),
231 ClassDefault::Unspecified => source.user(),
232 };
233
234 let role = match self.role_transition_new_role(source.role(), target.type_(), &policy_class)
235 {
236 Some(new_role) => new_role,
237 None => match class_defaults.role() {
238 ClassDefault::Source => source.role(),
239 ClassDefault::Target => target.role(),
240 ClassDefault::Unspecified => unspecified_role,
241 },
242 };
243
244 let type_ = override_type.unwrap_or_else(|| {
245 let transition = self
246 .access_vector_rules()
247 .find_type_rules(source.type_(), target.type_(), policy_class.id())
248 .find(|rule| rule.kind() == RuleKind::TypeTransition)
249 .map(|rule| rule.new_type());
250 match transition {
251 Some(new_type) => new_type,
252 None => match class_defaults.type_() {
253 ClassDefault::Source => source.type_(),
254 ClassDefault::Target => target.type_(),
255 ClassDefault::Unspecified => unspecified_type,
256 },
257 }
258 });
259
260 let (low_level, high_level) =
261 match self.range_transition_new_range(source.type_(), target.type_(), &policy_class) {
262 Some((low_level, high_level)) => (low_level, high_level),
263 None => match class_defaults.range() {
264 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
265 ClassDefaultRange::SourceHigh => {
266 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
267 }
268 ClassDefaultRange::SourceLowHigh => {
269 (source.low_level().clone(), source.high_level().cloned())
270 }
271 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
272 ClassDefaultRange::TargetHigh => {
273 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
274 }
275 ClassDefaultRange::TargetLowHigh => {
276 (target.low_level().clone(), target.high_level().cloned())
277 }
278 ClassDefaultRange::Unspecified => {
279 (unspecified_low.clone(), unspecified_high.cloned())
280 }
281 ClassDefaultRange::Glblub => glblub_range(source, target),
282 },
283 };
284
285 SecurityContext::new(user, role, type_, low_level, high_level)
287 }
288
289 pub(super) fn compute_access_decision(
292 &self,
293 source_context: &SecurityContext,
294 target_context: &SecurityContext,
295 target_class: &Class,
296 ) -> AccessDecision {
297 let mut access_decision = self.parsed_policy.compute_access_decision(
298 source_context,
299 target_context,
300 target_class,
301 );
302
303 if source_context.role() != target_context.role()
306 && Some(target_class.id()) == self.cached_process_class
307 {
308 let process_trans_perms = self.process_trans_perms();
309 if (access_decision.allow & process_trans_perms) != AccessVector::NONE
310 && !self.role_transition_is_explicitly_allowed(
311 source_context.role(),
312 target_context.role(),
313 )
314 {
315 access_decision.allow -= process_trans_perms;
319 }
320 }
321
322 access_decision
323 }
324
325 fn process_trans_perms(&self) -> AccessVector {
327 let mut perms = self
328 .kernel_permission_to_access_vector(ProcessPermission::Transition)
329 .unwrap_or(AccessVector::NONE);
330 perms |= self
331 .kernel_permission_to_access_vector(ProcessPermission::DynTransition)
332 .unwrap_or(AccessVector::NONE);
333 perms
334 }
335
336 pub(super) fn object_role(&self) -> RoleId {
339 self.cached_object_r_role
340 }
341
342 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
345 SecurityContext::from_policy_context(self.parsed_policy.initial_context(id))
347 }
348
349 pub(super) fn fs_use_label_and_type(
352 &self,
353 fs_type: NullessByteStr<'_>,
354 ) -> Option<FsUseLabelAndType> {
355 self.object_contexts()
356 .fs_uses()
357 .iter()
358 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
359 .map(|fs_use| FsUseLabelAndType {
360 context: SecurityContext::from_policy_context(fs_use.context()),
361 use_type: fs_use.behavior(),
362 })
363 }
364
365 pub(super) fn genfscon_label_for_fs_and_path(
370 &self,
371 fs_type: NullessByteStr<'_>,
372 node_path: NullessByteStr<'_>,
373 class: Option<crate::KernelClass>,
374 ) -> Option<SecurityContext> {
375 let node_path = if class == Some(crate::FileClass::LnkFile.into())
376 && !self.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
377 {
378 "/".into()
382 } else {
383 node_path
384 };
385
386 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
387
388 let fs_contexts = self.genfscon_find_all(fs_type.as_bytes());
390
391 #[derive(PartialEq)]
392 enum OrderType {
393 Alphabetic,
394 ByLength,
395 Unknown,
396 }
397 let mut result: Option<&GenfsConPath> = None;
410 let mut order_type = OrderType::Unknown;
411 let mut prev_path_bytes: Option<Vec<u8>> = None;
412 for fs_context in fs_contexts {
413 let path = fs_context.partial_path();
415 if order_type == OrderType::Unknown {
416 if let Some(prev) = &prev_path_bytes {
417 if path.len() > prev.len() {
418 order_type = OrderType::Alphabetic;
419 } else if path < prev.as_slice() {
420 order_type = OrderType::ByLength;
421 }
422 }
423 prev_path_bytes = Some(path.to_vec());
424 }
425
426 let class_matches = class_id.is_none()
428 || fs_context
429 .class()
430 .map(|other| other == class_id.unwrap().into())
431 .unwrap_or(true);
432 if !class_matches {
433 continue;
434 }
435
436 if order_type == OrderType::Alphabetic && fs_context.partial_path() > node_path.0 {
437 break;
442 }
443
444 if node_path.0.starts_with(fs_context.partial_path()) {
445 if result
446 .as_ref()
447 .map_or(true, |c| c.partial_path().len() < fs_context.partial_path().len())
448 {
449 result = Some(fs_context);
451 if order_type == OrderType::ByLength {
452 break;
453 }
454 }
455 }
456 }
457
458 Some(SecurityContext::from_policy_context(result?.context()))
461 }
462
463 fn role_transition_new_role(
464 &self,
465 current_role: RoleId,
466 type_: TypeId,
467 class: &Class,
468 ) -> Option<RoleId> {
469 self.role_transitions()
470 .iter()
471 .find(|role_transition| {
472 role_transition.current_role() == current_role
473 && role_transition.type_() == type_
474 && role_transition.class() == class.id().into()
475 })
476 .map(|x| x.new_role())
477 }
478
479 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
480 self.role_allowlist().iter().any(|role_allow| {
481 role_allow.source_role() == source_role && role_allow.new_role() == new_role
482 })
483 }
484
485 fn type_transition_new_type_with_name(
486 &self,
487 source_type: TypeId,
488 target_type: TypeId,
489 class: &Class,
490 name: &[u8],
491 ) -> Option<TypeId> {
492 self.compute_filename_transition(source_type, target_type, class.id().into(), name)
493 }
494
495 fn range_transition_new_range(
496 &self,
497 source_type: TypeId,
498 target_type: TypeId,
499 class: &Class,
500 ) -> Option<(MlsLevel, Option<MlsLevel>)> {
501 for range_transition in self.range_transitions() {
502 if range_transition.source_type() == source_type
503 && range_transition.target_type() == target_type
504 && range_transition.target_class() == class.id()
505 {
506 let mls_range = range_transition.mls_range();
507 let low_level = mls_range.low().clone();
508 let high_level = mls_range.high().clone();
509 return Some((low_level, high_level));
510 }
511 }
512
513 None
514 }
515}
516
517fn get_permission_id_by_name(
520 common_symbols: &IdAndNameIndexed<SymbolArray<CommonSymbol>>,
521 class: &Class,
522 name: &str,
523) -> Option<PermissionId> {
524 let name = name.as_bytes();
525 if let Some(permission) = class.permissions().iter().find(|p| p.name() == name) {
526 return Some(permission.id());
527 }
528 let common_name = class.common_name();
529 if !common_name.is_empty() {
530 let common_symbol = common_symbols.get_by_name(common_name)?;
531 let permission = common_symbol.permissions().iter().find(|p| p.name() == name)?;
532 return Some(permission.id());
533 }
534 None
535}
536
537fn intersect_categories(left: &MlsLevel, right: &MlsLevel) -> CategorySet {
539 let right_categories = right.categories();
540 CategorySet::from_ids(left.category_ids().filter(|id| right_categories.contains(*id)))
541}
542
543fn glblub_range(
549 source: &SecurityContext,
550 target: &SecurityContext,
551) -> (MlsLevel, Option<MlsLevel>) {
552 let source_low = source.low_level();
553 let source_high = source.effective_high_level();
554 let target_low = target.low_level();
555 let target_high = target.effective_high_level();
556
557 let low_level = MlsLevel::new(
558 std::cmp::max(source_low.sensitivity(), target_low.sensitivity()),
559 intersect_categories(source_low, target_low),
560 );
561 let high_level = MlsLevel::new(
562 std::cmp::min(source_high.sensitivity(), target_high.sensitivity()),
563 intersect_categories(source_high, target_high),
564 );
565 (low_level, Some(high_level))
566}
567
568impl Deref for PolicyIndex {
569 type Target = ParsedPolicy;
570
571 fn deref(&self) -> &Self::Target {
572 &self.parsed_policy
573 }
574}