1use super::arrays::{ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION, FsContext, FsUseType};
6use super::security_context::SecurityContext;
7use super::{AccessVector, ClassId, MlsLevel, ParsedPolicy, PermissionId, RoleId, TypeId};
8use crate::new_policy::traits::{HasName, HasPolicyId};
9use crate::new_policy::{
10 Class, ClassDefault, ClassDefaultRange, CommonSymbol, HandleUnknown, IdAndNameIndexed,
11 SymbolArray,
12};
13use crate::{ClassPermission as _, KernelClass, KernelPermission, NullessByteStr, PolicyCap};
14
15use std::collections::HashMap;
16use std::ops::Deref;
17
18use strum::VariantArray as _;
19
20pub struct FsUseLabelAndType {
22 pub context: SecurityContext,
23 pub use_type: FsUseType,
24}
25
26type KernelPermissionIdsArray = [Option<PermissionId>; 32];
28
29#[derive(Debug)]
37pub struct PolicyIndex {
38 classes: HashMap<KernelClass, ClassId>,
41 permissions: [KernelPermissionIdsArray; KernelClass::VARIANTS.len()],
43 parsed_policy: ParsedPolicy,
45 cached_object_r_role: RoleId,
47}
48
49impl PolicyIndex {
50 pub fn new(parsed_policy: ParsedPolicy) -> Result<Self, anyhow::Error> {
57 let policy_classes = parsed_policy.classes();
58 let common_symbols = parsed_policy.common_symbols();
59
60 let mut classes = HashMap::with_capacity(crate::KernelClass::VARIANTS.len());
61
62 for known_class in crate::KernelClass::VARIANTS {
66 match policy_classes.get_by_name(known_class.name().as_bytes()) {
67 Some(class) => {
68 classes.insert(*known_class, class.id());
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 classes.shrink_to_fit();
80
81 let mut permissions = [KernelPermissionIdsArray::default(); _];
85 for kernel_permission in crate::KernelPermission::all_variants() {
86 let kernel_class_name = kernel_permission.class().name();
87 if let Some(class) = policy_classes.get_by_name(kernel_class_name.as_bytes()) {
88 if let Some(permission_id) =
89 get_permission_id_by_name(common_symbols, class, kernel_permission.name())
90 {
91 let kernel_class_id = kernel_permission.class() as usize;
92 let kernel_permission_id = kernel_permission.id() as usize;
93 permissions[kernel_class_id][kernel_permission_id] = Some(permission_id);
94 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
95 return Err(anyhow::anyhow!(
96 "missing permission {:?}:{:?}",
97 kernel_class_name,
98 kernel_permission.name(),
99 ));
100 }
101 }
102 }
103
104 let cached_object_r_role = parsed_policy
106 .roles()
107 .get_by_name(b"object_r")
108 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
109 .id();
110
111 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
112
113 for initial_sids in crate::InitialSid::all_variants() {
115 index.resolve_initial_context(*initial_sids);
116 }
117
118 for fs_use in index.parsed_policy.fs_uses() {
120 SecurityContext::new_from_policy_context(fs_use.context());
121 }
122
123 Ok(index)
124 }
125
126 pub(super) fn class(&self, object_class: crate::ObjectClass) -> Option<&Class> {
129 match object_class {
130 crate::ObjectClass::Kernel(kernel_class) => {
131 let &class_id = self.classes.get(&kernel_class)?;
132 self.classes().get_by_id(class_id)
133 }
134 crate::ObjectClass::ClassId(class_id) => self.classes().get_by_id(class_id),
135 }
136 }
137
138 pub fn kernel_permission_to_access_vector<P: Into<KernelPermission>>(
140 &self,
141 permission: P,
142 ) -> Option<AccessVector> {
143 let permission = permission.into();
144 let class_index = permission.class() as usize;
145 let permission_index = permission.id() as usize;
146 let permission_id = self.permissions[class_index][permission_index]?;
147 Some(permission_id.into())
148 }
149
150 pub fn compute_create_context_with_name(
158 &self,
159 source: &SecurityContext,
160 target: &SecurityContext,
161 class: crate::ObjectClass,
162 name: NullessByteStr<'_>,
163 ) -> Option<SecurityContext> {
164 let policy_class = self.class(class)?;
165 let type_id = self.type_transition_new_type_with_name(
166 source.type_(),
167 target.type_(),
168 &policy_class,
169 name,
170 )?;
171 Some(self.new_security_context_internal(
172 source,
173 target,
174 class,
175 Some(type_id),
177 ))
178 }
179
180 pub fn compute_create_context(
195 &self,
196 source: &SecurityContext,
197 target: &SecurityContext,
198 class: crate::ObjectClass,
199 ) -> SecurityContext {
200 self.new_security_context_internal(source, target, class, None)
201 }
202
203 fn new_security_context_internal(
209 &self,
210 source: &SecurityContext,
211 target: &SecurityContext,
212 target_class: crate::ObjectClass,
213 override_type: Option<TypeId>,
214 ) -> SecurityContext {
215 let Some(policy_class) = self.class(target_class) else {
216 return SecurityContext::new(
221 source.user(),
222 self.cached_object_r_role,
223 target.type_(),
224 source.low_level().clone(),
225 None,
226 );
227 };
228
229 let is_process_or_socket =
230 policy_class.name() == b"process" || policy_class.common_name() == b"socket";
231 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
232 if is_process_or_socket {
233 (source.role(), source.type_(), source.low_level(), source.high_level())
234 } else {
235 (self.cached_object_r_role, target.type_(), source.low_level(), None)
236 };
237 let class_defaults = policy_class.defaults();
238
239 let user = match class_defaults.user() {
240 ClassDefault::Source => source.user(),
241 ClassDefault::Target => target.user(),
242 ClassDefault::Unspecified => source.user(),
243 };
244
245 let role = match self.role_transition_new_role(source.role(), target.type_(), &policy_class)
246 {
247 Some(new_role) => new_role,
248 None => match class_defaults.role() {
249 ClassDefault::Source => source.role(),
250 ClassDefault::Target => target.role(),
251 ClassDefault::Unspecified => unspecified_role,
252 },
253 };
254
255 let type_ = override_type.unwrap_or_else(|| {
256 match self.parsed_policy.access_vector_rules_find(
257 source.type_(),
258 target.type_(),
259 policy_class.id().into(),
260 ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION,
261 ) {
262 Some(new_type_rule) => new_type_rule.new_type().unwrap(),
263 None => match class_defaults.type_() {
264 ClassDefault::Source => source.type_(),
265 ClassDefault::Target => target.type_(),
266 ClassDefault::Unspecified => unspecified_type,
267 },
268 }
269 });
270
271 let (low_level, high_level) =
272 match self.range_transition_new_range(source.type_(), target.type_(), &policy_class) {
273 Some((low_level, high_level)) => (low_level, high_level),
274 None => match class_defaults.range() {
275 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
276 ClassDefaultRange::SourceHigh => {
277 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
278 }
279 ClassDefaultRange::SourceLowHigh => {
280 (source.low_level().clone(), source.high_level().cloned())
281 }
282 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
283 ClassDefaultRange::TargetHigh => {
284 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
285 }
286 ClassDefaultRange::TargetLowHigh => {
287 (target.low_level().clone(), target.high_level().cloned())
288 }
289 ClassDefaultRange::Unspecified => {
290 (unspecified_low.clone(), unspecified_high.cloned())
291 }
292 ClassDefaultRange::UnknownUsedValue => {
293 unreachable!("Invalid ClassDefaultRange in validated policy")
294 }
295 },
296 };
297
298 SecurityContext::new(user, role, type_, low_level, high_level)
300 }
301
302 pub(super) fn object_role(&self) -> RoleId {
305 self.cached_object_r_role
306 }
307
308 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
311 self.resolve_initial_context(id)
313 }
314
315 pub(super) fn fs_use_label_and_type(
318 &self,
319 fs_type: NullessByteStr<'_>,
320 ) -> Option<FsUseLabelAndType> {
321 self.parsed_policy
322 .fs_uses()
323 .iter()
324 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
325 .map(|fs_use| FsUseLabelAndType {
326 context: SecurityContext::new_from_policy_context(fs_use.context()),
327 use_type: fs_use.behavior(),
328 })
329 }
330
331 pub(super) fn genfscon_label_for_fs_and_path(
336 &self,
337 fs_type: NullessByteStr<'_>,
338 node_path: NullessByteStr<'_>,
339 class: Option<crate::KernelClass>,
340 ) -> Option<SecurityContext> {
341 let node_path = if class == Some(crate::FileClass::LnkFile.into())
342 && !self.parsed_policy.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
343 {
344 "/".into()
348 } else {
349 node_path
350 };
351
352 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
353
354 let fs_contexts = self
356 .parsed_policy
357 .genfscon_find_all(std::str::from_utf8(fs_type.as_bytes()).expect("fs type is valid"));
358
359 #[derive(PartialEq)]
360 enum OrderType {
361 Alphabetic,
362 ByLength,
363 Unknown,
364 }
365 let mut result: Option<FsContext> = None;
378 let mut order_type = OrderType::Unknown;
379 let mut prev_path_bytes: Option<Vec<u8>> = None;
380 for fs_context in fs_contexts {
381 let path = fs_context.partial_path();
383 if order_type == OrderType::Unknown {
384 if let Some(prev) = &prev_path_bytes {
385 if path.len() > prev.len() {
386 order_type = OrderType::Alphabetic;
387 } else if path < prev.as_slice() {
388 order_type = OrderType::ByLength;
389 }
390 }
391 prev_path_bytes = Some(path.to_vec());
392 }
393
394 let class_matches = class_id.is_none()
396 || fs_context
397 .class()
398 .map(|other| other == class_id.unwrap().into())
399 .unwrap_or(true);
400 if !class_matches {
401 continue;
402 }
403
404 if order_type == OrderType::Alphabetic && fs_context.partial_path() > node_path.0 {
405 break;
410 }
411
412 if node_path.0.starts_with(fs_context.partial_path()) {
413 if result
414 .as_ref()
415 .map_or(true, |c| c.partial_path().len() < fs_context.partial_path().len())
416 {
417 result = Some(fs_context);
419 if order_type == OrderType::ByLength {
420 break;
421 }
422 }
423 }
424 }
425
426 result.and_then(|fs_context| {
429 Some(SecurityContext::new_from_policy_context(fs_context.context()))
430 })
431 }
432
433 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
435 SecurityContext::new_from_policy_context(self.parsed_policy.initial_context(id))
436 }
437
438 fn role_transition_new_role(
439 &self,
440 current_role: RoleId,
441 type_: TypeId,
442 class: &Class,
443 ) -> Option<RoleId> {
444 self.parsed_policy
445 .role_transitions()
446 .iter()
447 .find(|role_transition| {
448 role_transition.current_role() == current_role
449 && role_transition.type_() == type_
450 && role_transition.class() == class.id().into()
451 })
452 .map(|x| x.new_role())
453 }
454
455 #[allow(dead_code)]
456 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
459 self.parsed_policy
460 .role_allowlist()
461 .iter()
462 .find(|role_allow| {
463 role_allow.source_role() == source_role && role_allow.new_role() == new_role
464 })
465 .is_some()
466 }
467
468 fn type_transition_new_type_with_name(
469 &self,
470 source_type: TypeId,
471 target_type: TypeId,
472 class: &Class,
473 name: NullessByteStr<'_>,
474 ) -> Option<TypeId> {
475 self.parsed_policy.compute_filename_transition(
476 source_type,
477 target_type,
478 class.id().into(),
479 name,
480 )
481 }
482
483 fn range_transition_new_range(
484 &self,
485 source_type: TypeId,
486 target_type: TypeId,
487 class: &Class,
488 ) -> Option<(MlsLevel, Option<MlsLevel>)> {
489 for range_transition in self.parsed_policy.range_transitions() {
490 if range_transition.source_type() == source_type
491 && range_transition.target_type() == target_type
492 && range_transition.target_class() == class.id().into()
493 {
494 let mls_range = range_transition.mls_range();
495 let low_level = mls_range.low().clone();
496 let high_level = mls_range.high().clone();
497 return Some((low_level, high_level));
498 }
499 }
500
501 None
502 }
503}
504
505fn get_permission_id_by_name(
508 common_symbols: &IdAndNameIndexed<SymbolArray<CommonSymbol>>,
509 class: &Class,
510 name: &str,
511) -> Option<PermissionId> {
512 let name = name.as_bytes();
513 if let Some(permission) = class.permissions().iter().find(|p| p.name() == name) {
514 return Some(permission.id());
515 }
516 let common_name = class.common_name();
517 if !common_name.is_empty() {
518 let common_symbol = common_symbols.get_by_name(common_name)?;
519 let permission = common_symbol.permissions().iter().find(|p| p.name() == name)?;
520 return Some(permission.id());
521 }
522 None
523}
524
525impl Deref for PolicyIndex {
526 type Target = ParsedPolicy;
527
528 fn deref(&self) -> &Self::Target {
529 &self.parsed_policy
530 }
531}