1use super::arrays::{ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION, FsContext, FsUseType};
6use super::metadata::HandleUnknown;
7use super::security_context::{SecurityContext, SecurityLevel};
8use super::symbols::{Class, ClassDefault, ClassDefaultRange, Classes, CommonSymbols};
9use super::{AccessVector, ClassPermissionId, ParsedPolicy, RoleId, TypeId};
10use crate::{ClassPermission as _, KernelPermission, NullessByteStr, PolicyCap};
11
12use std::collections::HashMap;
13
14pub struct FsUseLabelAndType {
16 pub context: SecurityContext,
17 pub use_type: FsUseType,
18}
19
20#[derive(Debug)]
28pub(super) struct PolicyIndex {
29 classes: HashMap<crate::ObjectClass, usize>,
35 permissions: HashMap<crate::KernelPermission, ClassPermissionId>,
37 parsed_policy: ParsedPolicy,
39 cached_object_r_role: RoleId,
41}
42
43impl PolicyIndex {
44 pub fn new(parsed_policy: ParsedPolicy) -> Result<Self, anyhow::Error> {
51 let policy_classes = parsed_policy.classes();
52 let common_symbols = parsed_policy.common_symbols();
53
54 let mut classes = HashMap::with_capacity(policy_classes.len() * 2);
58
59 for known_class in crate::KernelClass::all_variants() {
63 match get_class_index_by_name(policy_classes, known_class.name()) {
64 Some(class_index) => {
65 classes.insert(known_class.into(), class_index);
66 }
67 None => {
68 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
69 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
70 }
71 }
72 }
73 }
74
75 for index in 0..policy_classes.len() {
77 let class = &policy_classes[index];
78 classes.insert(class.id().into(), index);
79 }
80
81 classes.shrink_to_fit();
83
84 let mut permissions =
88 HashMap::with_capacity(crate::KernelPermission::all_variants().count());
89 for known_permission in crate::KernelPermission::all_variants() {
90 let object_class = known_permission.class();
91 if let Some(class_index) = classes.get(&object_class.into()) {
92 let class = &policy_classes[*class_index];
93 if let Some(permission_id) =
94 get_permission_id_by_name(common_symbols, class, known_permission.name())
95 {
96 permissions.insert(known_permission, permission_id);
97 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
98 return Err(anyhow::anyhow!(
99 "missing permission {:?}:{:?}",
100 object_class.name(),
101 known_permission.name(),
102 ));
103 }
104 }
105 }
106 permissions.shrink_to_fit();
107
108 let cached_object_r_role = parsed_policy
110 .role_by_name("object_r".into())
111 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
112 .id();
113
114 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
115
116 for initial_sids in crate::InitialSid::all_variants() {
118 index.resolve_initial_context(*initial_sids);
119 }
120
121 for fs_use in index.parsed_policy.fs_uses() {
123 SecurityContext::new_from_policy_context(fs_use.context());
124 }
125
126 Ok(index)
127 }
128
129 pub fn class<'a>(&'a self, object_class: crate::ObjectClass) -> Option<&'a Class> {
132 let index = self.classes.get(&object_class)?;
133 Some(&self.parsed_policy.classes()[*index])
134 }
135
136 pub fn kernel_permission_to_access_vector<P: Into<KernelPermission>>(
138 &self,
139 permission: P,
140 ) -> Option<AccessVector> {
141 let permission = permission.into();
142 let permission_index = self.permissions.get(&permission)?;
143 Some(AccessVector::from_class_permission_id(*permission_index))
144 }
145
146 pub fn compute_create_context_with_name(
154 &self,
155 source: &SecurityContext,
156 target: &SecurityContext,
157 class: crate::ObjectClass,
158 name: NullessByteStr<'_>,
159 ) -> Option<SecurityContext> {
160 let policy_class = self.class(class)?;
161 let type_id = self.type_transition_new_type_with_name(
162 source.type_(),
163 target.type_(),
164 policy_class,
165 name,
166 )?;
167 Some(self.new_security_context_internal(
168 source,
169 target,
170 class,
171 Some(type_id),
173 ))
174 }
175
176 pub fn compute_create_context(
191 &self,
192 source: &SecurityContext,
193 target: &SecurityContext,
194 class: crate::ObjectClass,
195 ) -> SecurityContext {
196 self.new_security_context_internal(source, target, class, None)
197 }
198
199 fn new_security_context_internal(
205 &self,
206 source: &SecurityContext,
207 target: &SecurityContext,
208 target_class: crate::ObjectClass,
209 override_type: Option<TypeId>,
210 ) -> SecurityContext {
211 let Some(policy_class) = self.class(target_class) else {
212 return SecurityContext::new(
217 source.user(),
218 self.cached_object_r_role,
219 target.type_(),
220 source.low_level().clone(),
221 None,
222 );
223 };
224
225 let is_process_or_socket = policy_class.name_bytes() == b"process"
226 || policy_class.common_name_bytes() == b"socket";
227 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
228 if is_process_or_socket {
229 (source.role(), source.type_(), source.low_level(), source.high_level())
230 } else {
231 (self.cached_object_r_role, target.type_(), source.low_level(), None)
232 };
233 let class_defaults = policy_class.defaults();
234
235 let user = match class_defaults.user() {
236 ClassDefault::Source => source.user(),
237 ClassDefault::Target => target.user(),
238 ClassDefault::Unspecified => source.user(),
239 };
240
241 let role = match self.role_transition_new_role(source.role(), target.type_(), policy_class)
242 {
243 Some(new_role) => new_role,
244 None => match class_defaults.role() {
245 ClassDefault::Source => source.role(),
246 ClassDefault::Target => target.role(),
247 ClassDefault::Unspecified => unspecified_role,
248 },
249 };
250
251 let type_ = override_type.unwrap_or_else(|| {
252 match self.parsed_policy.access_vector_rules_find(
253 source.type_(),
254 target.type_(),
255 policy_class.id(),
256 ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION,
257 ) {
258 Some(new_type_rule) => new_type_rule.new_type().unwrap(),
259 None => match class_defaults.type_() {
260 ClassDefault::Source => source.type_(),
261 ClassDefault::Target => target.type_(),
262 ClassDefault::Unspecified => unspecified_type,
263 },
264 }
265 });
266
267 let (low_level, high_level) =
268 match self.range_transition_new_range(source.type_(), target.type_(), policy_class) {
269 Some((low_level, high_level)) => (low_level, high_level),
270 None => match class_defaults.range() {
271 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
272 ClassDefaultRange::SourceHigh => {
273 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
274 }
275 ClassDefaultRange::SourceLowHigh => {
276 (source.low_level().clone(), source.high_level().cloned())
277 }
278 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
279 ClassDefaultRange::TargetHigh => {
280 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
281 }
282 ClassDefaultRange::TargetLowHigh => {
283 (target.low_level().clone(), target.high_level().cloned())
284 }
285 ClassDefaultRange::Unspecified => {
286 (unspecified_low.clone(), unspecified_high.cloned())
287 }
288 },
289 };
290
291 SecurityContext::new(user, role, type_, low_level, high_level)
293 }
294
295 pub(super) fn object_role(&self) -> RoleId {
298 self.cached_object_r_role
299 }
300
301 pub(super) fn parsed_policy(&self) -> &ParsedPolicy {
302 &self.parsed_policy
303 }
304
305 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
308 self.resolve_initial_context(id)
310 }
311
312 pub(super) fn fs_use_label_and_type(
315 &self,
316 fs_type: NullessByteStr<'_>,
317 ) -> Option<FsUseLabelAndType> {
318 self.parsed_policy
319 .fs_uses()
320 .iter()
321 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
322 .map(|fs_use| FsUseLabelAndType {
323 context: SecurityContext::new_from_policy_context(fs_use.context()),
324 use_type: fs_use.behavior(),
325 })
326 }
327
328 pub(super) fn genfscon_label_for_fs_and_path(
333 &self,
334 fs_type: NullessByteStr<'_>,
335 node_path: NullessByteStr<'_>,
336 class: Option<crate::KernelClass>,
337 ) -> Option<SecurityContext> {
338 let node_path = if class == Some(crate::FileClass::Link.into())
339 && !self.parsed_policy.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
340 {
341 "/".into()
345 } else {
346 node_path
347 };
348
349 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
350
351 let fs_contexts = self
353 .parsed_policy
354 .generic_fs_contexts()
355 .iter()
356 .find(|genfscon| genfscon.fs_type() == fs_type.as_bytes())?
357 .contexts();
358
359 let mut result: Option<&FsContext> = None;
372 for fs_context in fs_contexts {
373 if node_path.0.starts_with(fs_context.partial_path()) {
374 if result.is_none()
375 || result.unwrap().partial_path().len() < fs_context.partial_path().len()
376 {
377 if class_id.is_none()
378 || fs_context
379 .class()
380 .map(|other| other == class_id.unwrap())
381 .unwrap_or(true)
382 {
383 result = Some(fs_context);
384 }
385 }
386 }
387 }
388
389 result.and_then(|fs_context| {
392 Some(SecurityContext::new_from_policy_context(fs_context.context()))
393 })
394 }
395
396 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
398 SecurityContext::new_from_policy_context(self.parsed_policy().initial_context(id))
399 }
400
401 fn role_transition_new_role(
402 &self,
403 current_role: RoleId,
404 type_: TypeId,
405 class: &Class,
406 ) -> Option<RoleId> {
407 self.parsed_policy
408 .role_transitions()
409 .iter()
410 .find(|role_transition| {
411 role_transition.current_role() == current_role
412 && role_transition.type_() == type_
413 && role_transition.class() == class.id()
414 })
415 .map(|x| x.new_role())
416 }
417
418 #[allow(dead_code)]
419 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
422 self.parsed_policy
423 .role_allowlist()
424 .iter()
425 .find(|role_allow| {
426 role_allow.source_role() == source_role && role_allow.new_role() == new_role
427 })
428 .is_some()
429 }
430
431 fn type_transition_new_type_with_name(
432 &self,
433 source_type: TypeId,
434 target_type: TypeId,
435 class: &Class,
436 name: NullessByteStr<'_>,
437 ) -> Option<TypeId> {
438 self.parsed_policy.compute_filename_transition(source_type, target_type, class.id(), name)
439 }
440
441 fn range_transition_new_range(
442 &self,
443 source_type: TypeId,
444 target_type: TypeId,
445 class: &Class,
446 ) -> Option<(SecurityLevel, Option<SecurityLevel>)> {
447 for range_transition in self.parsed_policy.range_transitions() {
448 if range_transition.source_type() == source_type
449 && range_transition.target_type() == target_type
450 && range_transition.target_class() == class.id()
451 {
452 let mls_range = range_transition.mls_range();
453 let low_level = SecurityLevel::new_from_mls_level(mls_range.low());
454 let high_level = mls_range
455 .high()
456 .as_ref()
457 .map(|high_level| SecurityLevel::new_from_mls_level(high_level));
458 return Some((low_level, high_level));
459 }
460 }
461
462 None
463 }
464}
465
466fn get_class_index_by_name<'a>(classes: &'a Classes, name: &str) -> Option<usize> {
467 let name_bytes = name.as_bytes();
468 classes.iter().position(|class| class.name_bytes() == name_bytes)
469}
470
471fn get_permission_id_by_name(
474 common_symbols: &CommonSymbols,
475 class: &Class,
476 name: &str,
477) -> Option<ClassPermissionId> {
478 let name = name.as_bytes();
479 if let Some(permission) = class.permissions().iter().find(|p| p.name_bytes() == name) {
480 return Some(permission.id());
481 }
482 let common_name = class.common_name_bytes();
483 if !common_name.is_empty() {
484 let common_symbol = common_symbols.iter().find(|cs| cs.name_bytes() == common_name)?;
485 let permission = common_symbol.permissions().iter().find(|p| p.name_bytes() == name)?;
486 return Some(permission.id());
487 }
488 None
489}