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 .role_by_name("object_r".into())
107 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
108 .id();
109
110 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
111
112 for initial_sids in crate::InitialSid::all_variants() {
114 index.resolve_initial_context(*initial_sids);
115 }
116
117 for fs_use in index.parsed_policy.fs_uses() {
119 SecurityContext::new_from_policy_context(fs_use.context());
120 }
121
122 Ok(index)
123 }
124
125 pub(super) fn class(&self, object_class: crate::ObjectClass) -> Option<&Class> {
128 match object_class {
129 crate::ObjectClass::Kernel(kernel_class) => {
130 let &class_id = self.classes.get(&kernel_class)?;
131 self.classes().get_by_id(class_id)
132 }
133 crate::ObjectClass::ClassId(class_id) => self.classes().get_by_id(class_id),
134 }
135 }
136
137 pub fn kernel_permission_to_access_vector<P: Into<KernelPermission>>(
139 &self,
140 permission: P,
141 ) -> Option<AccessVector> {
142 let permission = permission.into();
143 let class_index = permission.class() as usize;
144 let permission_index = permission.id() as usize;
145 let permission_id = self.permissions[class_index][permission_index]?;
146 Some(permission_id.into())
147 }
148
149 pub fn compute_create_context_with_name(
157 &self,
158 source: &SecurityContext,
159 target: &SecurityContext,
160 class: crate::ObjectClass,
161 name: NullessByteStr<'_>,
162 ) -> Option<SecurityContext> {
163 let policy_class = self.class(class)?;
164 let type_id = self.type_transition_new_type_with_name(
165 source.type_(),
166 target.type_(),
167 &policy_class,
168 name,
169 )?;
170 Some(self.new_security_context_internal(
171 source,
172 target,
173 class,
174 Some(type_id),
176 ))
177 }
178
179 pub fn compute_create_context(
194 &self,
195 source: &SecurityContext,
196 target: &SecurityContext,
197 class: crate::ObjectClass,
198 ) -> SecurityContext {
199 self.new_security_context_internal(source, target, class, None)
200 }
201
202 fn new_security_context_internal(
208 &self,
209 source: &SecurityContext,
210 target: &SecurityContext,
211 target_class: crate::ObjectClass,
212 override_type: Option<TypeId>,
213 ) -> SecurityContext {
214 let Some(policy_class) = self.class(target_class) else {
215 return SecurityContext::new(
220 source.user(),
221 self.cached_object_r_role,
222 target.type_(),
223 source.low_level().clone(),
224 None,
225 );
226 };
227
228 let is_process_or_socket =
229 policy_class.name() == b"process" || policy_class.common_name() == b"socket";
230 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
231 if is_process_or_socket {
232 (source.role(), source.type_(), source.low_level(), source.high_level())
233 } else {
234 (self.cached_object_r_role, target.type_(), source.low_level(), None)
235 };
236 let class_defaults = policy_class.defaults();
237
238 let user = match class_defaults.user() {
239 ClassDefault::Source => source.user(),
240 ClassDefault::Target => target.user(),
241 ClassDefault::Unspecified => source.user(),
242 };
243
244 let role = match self.role_transition_new_role(source.role(), target.type_(), &policy_class)
245 {
246 Some(new_role) => new_role,
247 None => match class_defaults.role() {
248 ClassDefault::Source => source.role(),
249 ClassDefault::Target => target.role(),
250 ClassDefault::Unspecified => unspecified_role,
251 },
252 };
253
254 let type_ = override_type.unwrap_or_else(|| {
255 match self.parsed_policy.access_vector_rules_find(
256 source.type_(),
257 target.type_(),
258 policy_class.id().into(),
259 ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION,
260 ) {
261 Some(new_type_rule) => new_type_rule.new_type().unwrap(),
262 None => match class_defaults.type_() {
263 ClassDefault::Source => source.type_(),
264 ClassDefault::Target => target.type_(),
265 ClassDefault::Unspecified => unspecified_type,
266 },
267 }
268 });
269
270 let (low_level, high_level) =
271 match self.range_transition_new_range(source.type_(), target.type_(), &policy_class) {
272 Some((low_level, high_level)) => (low_level, high_level),
273 None => match class_defaults.range() {
274 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
275 ClassDefaultRange::SourceHigh => {
276 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
277 }
278 ClassDefaultRange::SourceLowHigh => {
279 (source.low_level().clone(), source.high_level().cloned())
280 }
281 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
282 ClassDefaultRange::TargetHigh => {
283 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
284 }
285 ClassDefaultRange::TargetLowHigh => {
286 (target.low_level().clone(), target.high_level().cloned())
287 }
288 ClassDefaultRange::Unspecified => {
289 (unspecified_low.clone(), unspecified_high.cloned())
290 }
291 ClassDefaultRange::UnknownUsedValue => {
292 unreachable!("Invalid ClassDefaultRange in validated policy")
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 initial_context(&self, id: crate::InitialSid) -> SecurityContext {
310 self.resolve_initial_context(id)
312 }
313
314 pub(super) fn fs_use_label_and_type(
317 &self,
318 fs_type: NullessByteStr<'_>,
319 ) -> Option<FsUseLabelAndType> {
320 self.parsed_policy
321 .fs_uses()
322 .iter()
323 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
324 .map(|fs_use| FsUseLabelAndType {
325 context: SecurityContext::new_from_policy_context(fs_use.context()),
326 use_type: fs_use.behavior(),
327 })
328 }
329
330 pub(super) fn genfscon_label_for_fs_and_path(
335 &self,
336 fs_type: NullessByteStr<'_>,
337 node_path: NullessByteStr<'_>,
338 class: Option<crate::KernelClass>,
339 ) -> Option<SecurityContext> {
340 let node_path = if class == Some(crate::FileClass::LnkFile.into())
341 && !self.parsed_policy.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
342 {
343 "/".into()
347 } else {
348 node_path
349 };
350
351 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
352
353 let fs_contexts = self
355 .parsed_policy
356 .genfscon_find_all(std::str::from_utf8(fs_type.as_bytes()).expect("fs type is valid"));
357
358 #[derive(PartialEq)]
359 enum OrderType {
360 Alphabetic,
361 ByLength,
362 Unknown,
363 }
364 let mut result: Option<FsContext> = None;
377 let mut order_type = OrderType::Unknown;
378 let mut prev_path_bytes: Option<Vec<u8>> = None;
379 for fs_context in fs_contexts {
380 let path = fs_context.partial_path();
382 if order_type == OrderType::Unknown {
383 if let Some(prev) = &prev_path_bytes {
384 if path.len() > prev.len() {
385 order_type = OrderType::Alphabetic;
386 } else if path < prev.as_slice() {
387 order_type = OrderType::ByLength;
388 }
389 }
390 prev_path_bytes = Some(path.to_vec());
391 }
392
393 let class_matches = class_id.is_none()
395 || fs_context
396 .class()
397 .map(|other| other == class_id.unwrap().into())
398 .unwrap_or(true);
399 if !class_matches {
400 continue;
401 }
402
403 if order_type == OrderType::Alphabetic && fs_context.partial_path() > node_path.0 {
404 break;
409 }
410
411 if node_path.0.starts_with(fs_context.partial_path()) {
412 if result
413 .as_ref()
414 .map_or(true, |c| c.partial_path().len() < fs_context.partial_path().len())
415 {
416 result = Some(fs_context);
418 if order_type == OrderType::ByLength {
419 break;
420 }
421 }
422 }
423 }
424
425 result.and_then(|fs_context| {
428 Some(SecurityContext::new_from_policy_context(fs_context.context()))
429 })
430 }
431
432 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
434 SecurityContext::new_from_policy_context(self.parsed_policy.initial_context(id))
435 }
436
437 fn role_transition_new_role(
438 &self,
439 current_role: RoleId,
440 type_: TypeId,
441 class: &Class,
442 ) -> Option<RoleId> {
443 self.parsed_policy
444 .role_transitions()
445 .iter()
446 .find(|role_transition| {
447 role_transition.current_role() == current_role
448 && role_transition.type_() == type_
449 && role_transition.class() == class.id().into()
450 })
451 .map(|x| x.new_role())
452 }
453
454 #[allow(dead_code)]
455 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
458 self.parsed_policy
459 .role_allowlist()
460 .iter()
461 .find(|role_allow| {
462 role_allow.source_role() == source_role && role_allow.new_role() == new_role
463 })
464 .is_some()
465 }
466
467 fn type_transition_new_type_with_name(
468 &self,
469 source_type: TypeId,
470 target_type: TypeId,
471 class: &Class,
472 name: NullessByteStr<'_>,
473 ) -> Option<TypeId> {
474 self.parsed_policy.compute_filename_transition(
475 source_type,
476 target_type,
477 class.id().into(),
478 name,
479 )
480 }
481
482 fn range_transition_new_range(
483 &self,
484 source_type: TypeId,
485 target_type: TypeId,
486 class: &Class,
487 ) -> Option<(MlsLevel, Option<MlsLevel>)> {
488 for range_transition in self.parsed_policy.range_transitions() {
489 if range_transition.source_type() == source_type
490 && range_transition.target_type() == target_type
491 && range_transition.target_class() == class.id().into()
492 {
493 let mls_range = range_transition.mls_range();
494 let low_level = mls_range.low().clone();
495 let high_level = mls_range.high().clone();
496 return Some((low_level, high_level));
497 }
498 }
499
500 None
501 }
502}
503
504fn get_permission_id_by_name(
507 common_symbols: &IdAndNameIndexed<SymbolArray<CommonSymbol>>,
508 class: &Class,
509 name: &str,
510) -> Option<PermissionId> {
511 let name = name.as_bytes();
512 if let Some(permission) = class.permissions().iter().find(|p| p.name_bytes() == name) {
513 return Some(permission.id());
514 }
515 let common_name = class.common_name();
516 if !common_name.is_empty() {
517 let common_symbol = common_symbols.get_by_name(common_name)?;
518 let permission = common_symbol.permissions().iter().find(|p| p.name_bytes() == name)?;
519 return Some(permission.id());
520 }
521 None
522}
523
524impl Deref for PolicyIndex {
525 type Target = ParsedPolicy;
526
527 fn deref(&self) -> &Self::Target {
528 &self.parsed_policy
529 }
530}