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, CommonSymbols, find_class_by_name};
9use super::{AccessVector, ClassId, ClassPermissionId, ParsedPolicy, RoleId, TypeId};
10use crate::{ClassPermission as _, KernelClass, KernelPermission, NullessByteStr, PolicyCap};
11
12use std::collections::HashMap;
13use strum::VariantArray as _;
14
15pub struct FsUseLabelAndType {
17 pub context: SecurityContext,
18 pub use_type: FsUseType,
19}
20
21type KernelPermissionIdsArray = [Option<ClassPermissionId>; 32];
23
24#[derive(Debug)]
32pub(super) struct PolicyIndex {
33 classes: HashMap<KernelClass, ClassId>,
36 permissions: [KernelPermissionIdsArray; KernelClass::VARIANTS.len()],
38 parsed_policy: ParsedPolicy,
40 cached_object_r_role: RoleId,
42}
43
44impl PolicyIndex {
45 pub fn new(parsed_policy: ParsedPolicy) -> Result<Self, anyhow::Error> {
52 let policy_classes = parsed_policy.classes();
53 let common_symbols = parsed_policy.common_symbols();
54
55 let mut classes = HashMap::with_capacity(crate::KernelClass::VARIANTS.len());
56
57 for known_class in crate::KernelClass::VARIANTS {
61 match find_class_by_name(&policy_classes, known_class.name()) {
62 Some(class) => {
63 classes.insert(*known_class, class.id());
64 }
65 None => {
66 if parsed_policy.handle_unknown() == HandleUnknown::Reject {
67 return Err(anyhow::anyhow!("missing object class {:?}", known_class,));
68 }
69 }
70 }
71 }
72
73 classes.shrink_to_fit();
75
76 let mut permissions = [KernelPermissionIdsArray::default(); _];
80 for kernel_permission in crate::KernelPermission::all_variants() {
81 let kernel_class_name = kernel_permission.class().name();
82 if let Some(class) = find_class_by_name(&policy_classes, kernel_class_name) {
83 if let Some(permission_id) =
84 get_permission_id_by_name(common_symbols, &class, kernel_permission.name())
85 {
86 let kernel_class_id = kernel_permission.class() as usize;
87 let kernel_permission_id = kernel_permission.id() as usize;
88 permissions[kernel_class_id][kernel_permission_id] = Some(permission_id);
89 } else if parsed_policy.handle_unknown() == HandleUnknown::Reject {
90 return Err(anyhow::anyhow!(
91 "missing permission {:?}:{:?}",
92 kernel_class_name,
93 kernel_permission.name(),
94 ));
95 }
96 }
97 }
98
99 let cached_object_r_role = parsed_policy
101 .role_by_name("object_r".into())
102 .ok_or_else(|| anyhow::anyhow!("missing 'object_r' role"))?
103 .id();
104
105 let index = Self { classes, permissions, parsed_policy, cached_object_r_role };
106
107 for initial_sids in crate::InitialSid::all_variants() {
109 index.resolve_initial_context(*initial_sids);
110 }
111
112 for fs_use in index.parsed_policy.fs_uses() {
114 SecurityContext::new_from_policy_context(fs_use.context());
115 }
116
117 Ok(index)
118 }
119
120 pub fn class(&self, object_class: crate::ObjectClass) -> Option<Class> {
123 match object_class {
124 crate::ObjectClass::Kernel(kernel_class) => {
125 let &class_id = self.classes.get(&kernel_class)?;
126 self.parsed_policy.class(class_id)
127 }
128 crate::ObjectClass::ClassId(class_id) => self.parsed_policy.class(class_id),
129 }
130 }
131
132 pub fn kernel_permission_to_access_vector<P: Into<KernelPermission>>(
134 &self,
135 permission: P,
136 ) -> Option<AccessVector> {
137 let permission = permission.into();
138 let class_index = permission.class() as usize;
139 let permission_index = permission.id() as usize;
140 let permission_id = self.permissions[class_index][permission_index]?;
141 Some(AccessVector::from_class_permission_id(permission_id))
142 }
143
144 pub fn compute_create_context_with_name(
152 &self,
153 source: &SecurityContext,
154 target: &SecurityContext,
155 class: crate::ObjectClass,
156 name: NullessByteStr<'_>,
157 ) -> Option<SecurityContext> {
158 let policy_class = self.class(class)?;
159 let type_id = self.type_transition_new_type_with_name(
160 source.type_(),
161 target.type_(),
162 &policy_class,
163 name,
164 )?;
165 Some(self.new_security_context_internal(
166 source,
167 target,
168 class,
169 Some(type_id),
171 ))
172 }
173
174 pub fn compute_create_context(
189 &self,
190 source: &SecurityContext,
191 target: &SecurityContext,
192 class: crate::ObjectClass,
193 ) -> SecurityContext {
194 self.new_security_context_internal(source, target, class, None)
195 }
196
197 fn new_security_context_internal(
203 &self,
204 source: &SecurityContext,
205 target: &SecurityContext,
206 target_class: crate::ObjectClass,
207 override_type: Option<TypeId>,
208 ) -> SecurityContext {
209 let Some(policy_class) = self.class(target_class) else {
210 return SecurityContext::new(
215 source.user(),
216 self.cached_object_r_role,
217 target.type_(),
218 source.low_level().clone(),
219 None,
220 );
221 };
222
223 let is_process_or_socket = policy_class.name_bytes() == b"process"
224 || policy_class.common_name_bytes() == b"socket";
225 let (unspecified_role, unspecified_type, unspecified_low, unspecified_high) =
226 if is_process_or_socket {
227 (source.role(), source.type_(), source.low_level(), source.high_level())
228 } else {
229 (self.cached_object_r_role, target.type_(), source.low_level(), None)
230 };
231 let class_defaults = policy_class.defaults();
232
233 let user = match class_defaults.user() {
234 ClassDefault::Source => source.user(),
235 ClassDefault::Target => target.user(),
236 ClassDefault::Unspecified => source.user(),
237 };
238
239 let role = match self.role_transition_new_role(source.role(), target.type_(), &policy_class)
240 {
241 Some(new_role) => new_role,
242 None => match class_defaults.role() {
243 ClassDefault::Source => source.role(),
244 ClassDefault::Target => target.role(),
245 ClassDefault::Unspecified => unspecified_role,
246 },
247 };
248
249 let type_ = override_type.unwrap_or_else(|| {
250 match self.parsed_policy.access_vector_rules_find(
251 source.type_(),
252 target.type_(),
253 policy_class.id(),
254 ACCESS_VECTOR_RULE_TYPE_TYPE_TRANSITION,
255 ) {
256 Some(new_type_rule) => new_type_rule.new_type().unwrap(),
257 None => match class_defaults.type_() {
258 ClassDefault::Source => source.type_(),
259 ClassDefault::Target => target.type_(),
260 ClassDefault::Unspecified => unspecified_type,
261 },
262 }
263 });
264
265 let (low_level, high_level) =
266 match self.range_transition_new_range(source.type_(), target.type_(), &policy_class) {
267 Some((low_level, high_level)) => (low_level, high_level),
268 None => match class_defaults.range() {
269 ClassDefaultRange::SourceLow => (source.low_level().clone(), None),
270 ClassDefaultRange::SourceHigh => {
271 (source.high_level().unwrap_or_else(|| source.low_level()).clone(), None)
272 }
273 ClassDefaultRange::SourceLowHigh => {
274 (source.low_level().clone(), source.high_level().cloned())
275 }
276 ClassDefaultRange::TargetLow => (target.low_level().clone(), None),
277 ClassDefaultRange::TargetHigh => {
278 (target.high_level().unwrap_or_else(|| target.low_level()).clone(), None)
279 }
280 ClassDefaultRange::TargetLowHigh => {
281 (target.low_level().clone(), target.high_level().cloned())
282 }
283 ClassDefaultRange::Unspecified => {
284 (unspecified_low.clone(), unspecified_high.cloned())
285 }
286 },
287 };
288
289 SecurityContext::new(user, role, type_, low_level, high_level)
291 }
292
293 pub(super) fn object_role(&self) -> RoleId {
296 self.cached_object_r_role
297 }
298
299 pub(super) fn parsed_policy(&self) -> &ParsedPolicy {
300 &self.parsed_policy
301 }
302
303 pub(super) fn initial_context(&self, id: crate::InitialSid) -> SecurityContext {
306 self.resolve_initial_context(id)
308 }
309
310 pub(super) fn fs_use_label_and_type(
313 &self,
314 fs_type: NullessByteStr<'_>,
315 ) -> Option<FsUseLabelAndType> {
316 self.parsed_policy
317 .fs_uses()
318 .iter()
319 .find(|fs_use| fs_use.fs_type() == fs_type.as_bytes())
320 .map(|fs_use| FsUseLabelAndType {
321 context: SecurityContext::new_from_policy_context(fs_use.context()),
322 use_type: fs_use.behavior(),
323 })
324 }
325
326 pub(super) fn genfscon_label_for_fs_and_path(
331 &self,
332 fs_type: NullessByteStr<'_>,
333 node_path: NullessByteStr<'_>,
334 class: Option<crate::KernelClass>,
335 ) -> Option<SecurityContext> {
336 let node_path = if class == Some(crate::FileClass::Link.into())
337 && !self.parsed_policy.has_policycap(PolicyCap::GenfsSeclabelSymlinks)
338 {
339 "/".into()
343 } else {
344 node_path
345 };
346
347 let class_id = class.and_then(|class| self.class(class.into())).map(|class| class.id());
348
349 let fs_contexts = self
351 .parsed_policy
352 .generic_fs_contexts()
353 .iter()
354 .find(|genfscon| genfscon.fs_type() == fs_type.as_bytes())?
355 .contexts();
356
357 let mut result: Option<&FsContext> = None;
370 for fs_context in fs_contexts {
371 if node_path.0.starts_with(fs_context.partial_path()) {
372 if result.is_none()
373 || result.unwrap().partial_path().len() < fs_context.partial_path().len()
374 {
375 if class_id.is_none()
376 || fs_context
377 .class()
378 .map(|other| other == class_id.unwrap())
379 .unwrap_or(true)
380 {
381 result = Some(fs_context);
382 }
383 }
384 }
385 }
386
387 result.and_then(|fs_context| {
390 Some(SecurityContext::new_from_policy_context(fs_context.context()))
391 })
392 }
393
394 fn resolve_initial_context(&self, id: crate::InitialSid) -> SecurityContext {
396 SecurityContext::new_from_policy_context(self.parsed_policy().initial_context(id))
397 }
398
399 fn role_transition_new_role(
400 &self,
401 current_role: RoleId,
402 type_: TypeId,
403 class: &Class,
404 ) -> Option<RoleId> {
405 self.parsed_policy
406 .role_transitions()
407 .iter()
408 .find(|role_transition| {
409 role_transition.current_role() == current_role
410 && role_transition.type_() == type_
411 && role_transition.class() == class.id()
412 })
413 .map(|x| x.new_role())
414 }
415
416 #[allow(dead_code)]
417 fn role_transition_is_explicitly_allowed(&self, source_role: RoleId, new_role: RoleId) -> bool {
420 self.parsed_policy
421 .role_allowlist()
422 .iter()
423 .find(|role_allow| {
424 role_allow.source_role() == source_role && role_allow.new_role() == new_role
425 })
426 .is_some()
427 }
428
429 fn type_transition_new_type_with_name(
430 &self,
431 source_type: TypeId,
432 target_type: TypeId,
433 class: &Class,
434 name: NullessByteStr<'_>,
435 ) -> Option<TypeId> {
436 self.parsed_policy.compute_filename_transition(source_type, target_type, class.id(), name)
437 }
438
439 fn range_transition_new_range(
440 &self,
441 source_type: TypeId,
442 target_type: TypeId,
443 class: &Class,
444 ) -> Option<(SecurityLevel, Option<SecurityLevel>)> {
445 for range_transition in self.parsed_policy.range_transitions() {
446 if range_transition.source_type() == source_type
447 && range_transition.target_type() == target_type
448 && range_transition.target_class() == class.id()
449 {
450 let mls_range = range_transition.mls_range();
451 let low_level = SecurityLevel::new_from_mls_level(mls_range.low());
452 let high_level = mls_range
453 .high()
454 .as_ref()
455 .map(|high_level| SecurityLevel::new_from_mls_level(high_level));
456 return Some((low_level, high_level));
457 }
458 }
459
460 None
461 }
462}
463
464fn get_permission_id_by_name(
467 common_symbols: &CommonSymbols,
468 class: &Class,
469 name: &str,
470) -> Option<ClassPermissionId> {
471 let name = name.as_bytes();
472 if let Some(permission) = class.permissions().iter().find(|p| p.name_bytes() == name) {
473 return Some(permission.id());
474 }
475 let common_name = class.common_name_bytes();
476 if !common_name.is_empty() {
477 let common_symbol = common_symbols.iter().find(|cs| cs.name_bytes() == common_name)?;
478 let permission = common_symbol.permissions().iter().find(|p| p.name_bytes() == name)?;
479 return Some(permission.id());
480 }
481 None
482}