1use super::arrays::{
6 ConditionalNode, Context, DeprecatedFilenameTransition, FilenameTransition,
7 FilenameTransitionList, FsUse, GenericFsContext, IPv6Node, InfinitiBandEndPort,
8 InfinitiBandPartitionKey, InitialSid, MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY,
9 NamedContextPair, Node, Port, RangeTransition, RoleAllow, RoleAllows, RoleTransition,
10 RoleTransitions, SimpleArray,
11};
12use super::error::{ParseError, ValidateError};
13use super::extensible_bitmap::ExtensibleBitmap;
14
15use super::constraints::evaluate_constraint;
16use super::parser::{PolicyCursor, PolicyData};
17use super::security_context::SecurityContext;
18use super::view::Hashable;
19use super::{
20 AccessDecision, AccessVector, CategoryId, ClassId, MlsLevel, Parse, PolicyValidationContext,
21 RoleId, SELINUX_AVD_FLAGS_PERMISSIVE, SensitivityId, TypeId, UserId, Validate,
22 XpermsAccessDecision, XpermsKind,
23};
24
25use crate::new_policy::rules::{
26 ExtendedPermissions, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES, XPERMS_TYPE_IOCTL_PREFIXES,
27 XPERMS_TYPE_NLMSG, XpermsBitmap,
28};
29use crate::new_policy::traits::{HasPolicyId, PolicyId};
30use crate::new_policy::{Class, NewPolicy};
31use crate::policy::arrays::FsContext;
32use crate::policy::view::CustomKeyHashedView;
33use crate::{NullessByteStr, PolicyCap};
34use std::ops::Deref;
35use std::sync::Arc;
36
37use anyhow::Context as _;
38use itertools::Itertools;
39use std::collections::HashSet;
40use std::fmt::Debug;
41use std::hash::Hash;
42use std::iter::Iterator;
43use zerocopy::little_endian as le;
44
45const MAXIMUM_POLICY_SIZE: usize = 1 << 24;
47
48#[derive(Debug)]
50pub struct ParsedPolicy {
51 data: PolicyData,
53
54 new_policy: Arc<NewPolicy>,
56
57 conditional_lists: SimpleArray<ConditionalNode>,
58 role_transitions: RoleTransitions,
60 role_allowlist: RoleAllows,
62 filename_transition_list: FilenameTransitionList,
63 initial_sids: SimpleArray<InitialSid>,
64 filesystems: SimpleArray<NamedContextPair>,
65 ports: SimpleArray<Port>,
66 network_interfaces: SimpleArray<NamedContextPair>,
67 nodes: SimpleArray<Node>,
68 fs_uses: SimpleArray<FsUse>,
69 ipv6_nodes: SimpleArray<IPv6Node>,
70 infinitiband_partition_keys: Option<SimpleArray<InfinitiBandPartitionKey>>,
71 infinitiband_end_ports: Option<SimpleArray<InfinitiBandEndPort>>,
72 generic_fs_contexts: CustomKeyHashedView<GenericFsContext>,
75 range_transitions: SimpleArray<RangeTransition>,
76 attribute_maps: Vec<ExtensibleBitmap>,
78}
79
80impl Deref for ParsedPolicy {
81 type Target = NewPolicy;
82 fn deref(&self) -> &Self::Target {
83 &self.new_policy
84 }
85}
86
87impl ParsedPolicy {
88 pub fn has_policycap(&self, policy_cap: PolicyCap) -> bool {
90 self.new_policy.policy_capabilities().is_set(policy_cap as u32)
91 }
92
93 pub(super) fn compute_access_decision(
105 &self,
106 source_context: &SecurityContext,
107 target_context: &SecurityContext,
108 target_class: &Class,
109 ) -> AccessDecision {
110 let mut access_decision = self.compute_explicitly_allowed(
111 source_context.type_(),
112 target_context.type_(),
113 target_class,
114 );
115 access_decision.allow -=
116 self.compute_denied_by_constraints(source_context, target_context, target_class);
117 access_decision
118 }
119
120 pub(super) fn compute_explicitly_allowed(
125 &self,
126 source_type: TypeId,
127 target_type: TypeId,
128 target_class: &Class,
129 ) -> AccessDecision {
130 let target_class_id = target_class.id();
131
132 let mut computed_access_vector = AccessVector::NONE;
133 let mut computed_audit_allow = AccessVector::NONE;
134 let mut computed_audit_deny = AccessVector::ALL;
135
136 let source_attribute_bitmap: &ExtensibleBitmap =
137 &self.attribute_maps[(source_type.as_u32() - 1) as usize];
138 let target_attribute_bitmap: &ExtensibleBitmap =
139 &self.attribute_maps[(target_type.as_u32() - 1) as usize];
140
141 for (source_bit_index, target_bit_index) in Itertools::cartesian_product(
142 source_attribute_bitmap.indices_of_set_bits(),
143 target_attribute_bitmap.indices_of_set_bits(),
144 ) {
145 let source_id = TypeId::from_u32((source_bit_index + 1) as u32).unwrap();
146 let target_id = TypeId::from_u32((target_bit_index + 1) as u32).unwrap();
147
148 let decisions = self.new_policy.access_vector_rules().find_av_decisions(
149 source_id,
150 target_id,
151 target_class_id,
152 );
153
154 if let Some(allow) = decisions.allow {
155 computed_access_vector |= allow;
156 }
157 if let Some(auditallow) = decisions.auditallow {
158 computed_audit_allow |= auditallow;
159 }
160 if let Some(dontaudit) = decisions.dontaudit {
161 computed_audit_deny &= dontaudit;
162 }
163 }
164
165 if let Some(parent) = self.types().get_by_id(source_type).unwrap().bounded_by() {
169 let access = if source_type == target_type {
172 self.compute_explicitly_allowed(parent, parent, target_class)
173 } else {
174 self.compute_explicitly_allowed(parent, target_type, target_class)
175 };
176 computed_access_vector &= access.allow;
177 }
178
179 let mut flags = 0;
180 if self.permissive_map().contains(source_type) {
181 flags |= SELINUX_AVD_FLAGS_PERMISSIVE;
182 }
183 AccessDecision {
184 allow: computed_access_vector,
185 auditallow: computed_audit_allow,
186 auditdeny: computed_audit_deny,
187 flags,
188 todo_bug: None,
189 }
190 }
191
192 fn compute_denied_by_constraints(
194 &self,
195 source_context: &SecurityContext,
196 target_context: &SecurityContext,
197 target_class: &Class,
198 ) -> AccessVector {
199 let mut denied = AccessVector::NONE;
200 for constraint in target_class.constraints() {
201 if !evaluate_constraint(constraint.constraint_expr(), source_context, target_context) {
202 denied |= constraint.access_vector();
203 }
204 }
205 denied
206 }
207
208 pub(super) fn compute_xperms_access_decision(
211 &self,
212 xperms_kind: XpermsKind,
213 source_context: &SecurityContext,
214 target_context: &SecurityContext,
215 target_class: &Class,
216 xperms_prefix: u8,
217 ) -> XpermsAccessDecision {
218 let target_class_id = target_class.id();
219
220 let mut explicit_allow: Option<XpermsBitmap> = None;
221 let mut auditallow = XpermsBitmap::NONE;
222 let mut auditdeny = XpermsBitmap::ALL;
223
224 let xperms_types = match xperms_kind {
225 XpermsKind::Ioctl => {
226 [XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES, XPERMS_TYPE_IOCTL_PREFIXES].as_slice()
227 }
228 XpermsKind::Nlmsg => [XPERMS_TYPE_NLMSG].as_slice(),
229 };
230 let bitmap_if_prefix_matches =
231 |xperms_prefix: u8, xperms: &ExtendedPermissions| match xperms_kind {
232 XpermsKind::Ioctl => match xperms.xperms_type() {
233 XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES => (xperms.xperms_optional_prefix()
234 == xperms_prefix)
235 .then_some(*xperms.xperms_bitmap()),
236 XPERMS_TYPE_IOCTL_PREFIXES => {
237 xperms.xperms_bitmap().contains(xperms_prefix).then_some(XpermsBitmap::ALL)
238 }
239 _ => None,
240 },
241 XpermsKind::Nlmsg => match xperms.xperms_type() {
242 XPERMS_TYPE_NLMSG => (xperms.xperms_optional_prefix() == xperms_prefix)
243 .then_some(*xperms.xperms_bitmap()),
244 _ => None,
245 },
246 };
247
248 let source_attribute_bitmap: &ExtensibleBitmap =
249 &self.attribute_maps[(source_context.type_().as_u32() - 1) as usize];
250 let target_attribute_bitmap: &ExtensibleBitmap =
251 &self.attribute_maps[(target_context.type_().as_u32() - 1) as usize];
252
253 for (source_bit_index, target_bit_index) in Itertools::cartesian_product(
254 source_attribute_bitmap.indices_of_set_bits(),
255 target_attribute_bitmap.indices_of_set_bits(),
256 ) {
257 let source_id = TypeId::from_u32((source_bit_index + 1) as u32).unwrap();
258 let target_id = TypeId::from_u32((target_bit_index + 1) as u32).unwrap();
259
260 let decisions = self.new_policy.access_vector_rules().find_xperms_decisions(
261 source_id,
262 target_id,
263 target_class_id,
264 );
265
266 for xperms in decisions.allow {
267 if xperms_types.contains(&xperms.xperms_type()) {
268 explicit_allow.get_or_insert(XpermsBitmap::NONE);
269 }
270 if let Some(xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
271 (*explicit_allow.get_or_insert(XpermsBitmap::NONE)) |= xperms_bitmap;
272 }
273 }
274
275 for xperms in decisions.auditallow {
276 if let Some(xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
277 auditallow |= xperms_bitmap;
278 }
279 }
280
281 for xperms in decisions.dontaudit {
282 if let Some(xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
283 auditdeny -= xperms_bitmap;
284 }
285 }
286 }
287 let allow = explicit_allow.unwrap_or(XpermsBitmap::ALL);
288 XpermsAccessDecision { allow, auditallow, auditdeny }
289 }
290
291 pub(super) fn initial_context(&self, mut id: crate::InitialSid) -> &Context {
293 if id == crate::InitialSid::Init && !self.has_policycap(PolicyCap::UserspaceInitialContext)
295 {
296 id = crate::InitialSid::Kernel
297 }
298
299 let id = le::U32::from(id as u32);
301 &self.initial_sids.data.iter().find(|initial| initial.id() == id).unwrap().context()
302 }
303
304 pub(super) fn fs_uses(&self) -> &[FsUse] {
305 &self.fs_uses.data
306 }
307
308 pub(super) fn genfscon_find_all(&self, fs_type: &str) -> impl Iterator<Item = FsContext> {
309 let query = GenericFsContext::for_query(fs_type);
310 self.generic_fs_contexts.find_all(query, &self.data)
311 }
312
313 pub(super) fn role_allowlist(&self) -> &[RoleAllow] {
314 &self.role_allowlist.data
315 }
316
317 pub(super) fn role_transitions(&self) -> &[RoleTransition] {
318 &self.role_transitions.data
319 }
320
321 pub(super) fn range_transitions(&self) -> &[RangeTransition] {
322 &self.range_transitions.data
323 }
324
325 pub(super) fn compute_filename_transition(
326 &self,
327 source_type: TypeId,
328 target_type: TypeId,
329 class: ClassId,
330 name: NullessByteStr<'_>,
331 ) -> Option<TypeId> {
332 match &self.filename_transition_list {
333 FilenameTransitionList::PolicyVersionGeq33(list) => {
334 let entry = list.data.iter().find(|transition| {
335 transition.target_type() == target_type
336 && transition.target_class() == class
337 && transition.name_bytes() == name.as_bytes()
338 })?;
339 entry
340 .outputs()
341 .iter()
342 .find(|entry| entry.has_source_type(source_type))
343 .map(|x| x.out_type())
344 }
345 FilenameTransitionList::PolicyVersionLeq32(list) => list
346 .data
347 .iter()
348 .find(|transition| {
349 transition.target_class() == class
350 && transition.target_type() == target_type
351 && transition.source_type() == source_type
352 && transition.name_bytes() == name.as_bytes()
353 })
354 .map(|x| x.out_type()),
355 }
356 }
357
358 fn validate_mls_level(
361 &self,
362 level: &MlsLevel,
363 sensitivity_ids: &HashSet<SensitivityId>,
364 category_ids: &HashSet<CategoryId>,
365 ) -> Result<(), anyhow::Error> {
366 validate_id(sensitivity_ids, level.sensitivity(), "sensitivity")?;
367 for id in level.category_ids() {
368 validate_id(category_ids, id, "category")?;
369 }
370 Ok(())
371 }
372
373 fn validate_mls_range(
380 &self,
381 low_level: &MlsLevel,
382 high_level: &Option<MlsLevel>,
383 sensitivity_ids: &HashSet<SensitivityId>,
384 category_ids: &HashSet<CategoryId>,
385 ) -> Result<(), anyhow::Error> {
386 self.validate_mls_level(low_level, sensitivity_ids, category_ids)?;
387 if let Some(high) = high_level {
388 self.validate_mls_level(high, sensitivity_ids, category_ids)?;
389 if !high.dominates(low_level) {
390 return Err(ValidateError::InvalidMlsRange {
391 low: low_level.to_string(self).into(),
392 high: high.to_string(self).into(),
393 }
394 .into());
395 }
396 }
397 Ok(())
398 }
399
400 fn validate_context(
401 &self,
402 context: &Context,
403 user_ids: &HashSet<UserId>,
404 role_ids: &HashSet<RoleId>,
405 type_ids: &HashSet<TypeId>,
406 sensitivity_ids: &HashSet<SensitivityId>,
407 category_ids: &HashSet<CategoryId>,
408 ) -> Result<(), anyhow::Error> {
409 validate_id(user_ids, context.user_id(), "user")?;
410 validate_id(role_ids, context.role_id(), "role")?;
411 validate_id(type_ids, context.type_id(), "type")?;
412 self.validate_mls_range(
413 context.low_level(),
414 context.high_level(),
415 sensitivity_ids,
416 category_ids,
417 )?;
418 Ok(())
419 }
420}
421
422impl ParsedPolicy {
423 pub(super) fn parse(data: PolicyData) -> Result<Self, anyhow::Error> {
426 let policy_size = data.len();
427 if MAXIMUM_POLICY_SIZE <= policy_size {
428 return Err(anyhow::Error::from(ParseError::UnsupportedlyLarge {
429 observed: policy_size,
430 limit: MAXIMUM_POLICY_SIZE,
431 }));
432 }
433 let new_policy =
434 NewPolicy::parse(&data).map_err(|e| anyhow::anyhow!("new parser failed: {:?}", e))?;
435 new_policy.validate().context("validating new policy structure")?;
436
437 let rest_data = new_policy.rest_bytes();
438 let (policy, excess_bytes) = parse_policy_remaining(new_policy, rest_data)?;
439 if excess_bytes > 0 {
440 return Err(anyhow::Error::from(ParseError::TrailingBytes { num_bytes: excess_bytes }));
441 }
442 Ok(policy)
443 }
444}
445
446fn parse_policy_remaining(
448 new_policy: NewPolicy,
449 rest_data: PolicyData,
450) -> Result<(ParsedPolicy, usize), anyhow::Error> {
451 let tail = PolicyCursor::new(&rest_data);
452
453 let (conditional_lists, tail) = SimpleArray::<ConditionalNode>::parse(tail)
454 .map_err(Into::<anyhow::Error>::into)
455 .context("parsing conditional lists")?;
456
457 let (role_transitions, tail) = RoleTransitions::parse(tail)
458 .map_err(Into::<anyhow::Error>::into)
459 .context("parsing role transitions")?;
460
461 let (role_allowlist, tail) = RoleAllows::parse(tail)
462 .map_err(Into::<anyhow::Error>::into)
463 .context("parsing role allow rules")?;
464
465 let (filename_transition_list, tail) = if new_policy.policy_version() >= 33 {
466 let (filename_transition_list, tail) = SimpleArray::<FilenameTransition>::parse(tail)
467 .map_err(Into::<anyhow::Error>::into)
468 .context("parsing standard filename transitions")?;
469 (FilenameTransitionList::PolicyVersionGeq33(filename_transition_list), tail)
470 } else {
471 let (filename_transition_list, tail) =
472 SimpleArray::<DeprecatedFilenameTransition>::parse(tail)
473 .map_err(Into::<anyhow::Error>::into)
474 .context("parsing deprecated filename transitions")?;
475 (FilenameTransitionList::PolicyVersionLeq32(filename_transition_list), tail)
476 };
477
478 let (initial_sids, tail) = SimpleArray::<InitialSid>::parse(tail)
479 .map_err(Into::<anyhow::Error>::into)
480 .context("parsing initial sids")?;
481
482 let (filesystems, tail) = SimpleArray::<NamedContextPair>::parse(tail)
483 .map_err(Into::<anyhow::Error>::into)
484 .context("parsing filesystem contexts")?;
485
486 let (ports, tail) = SimpleArray::<Port>::parse(tail)
487 .map_err(Into::<anyhow::Error>::into)
488 .context("parsing ports")?;
489
490 let (network_interfaces, tail) = SimpleArray::<NamedContextPair>::parse(tail)
491 .map_err(Into::<anyhow::Error>::into)
492 .context("parsing network interfaces")?;
493
494 let (nodes, tail) = SimpleArray::<Node>::parse(tail)
495 .map_err(Into::<anyhow::Error>::into)
496 .context("parsing nodes")?;
497
498 let (fs_uses, tail) = SimpleArray::<FsUse>::parse(tail)
499 .map_err(Into::<anyhow::Error>::into)
500 .context("parsing fs uses")?;
501
502 let (ipv6_nodes, tail) = SimpleArray::<IPv6Node>::parse(tail)
503 .map_err(Into::<anyhow::Error>::into)
504 .context("parsing ipv6 nodes")?;
505
506 let (infinitiband_partition_keys, infinitiband_end_ports, tail) =
507 if new_policy.policy_version() >= MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY {
508 let (infinity_band_partition_keys, tail) =
509 SimpleArray::<InfinitiBandPartitionKey>::parse(tail)
510 .map_err(Into::<anyhow::Error>::into)
511 .context("parsing infiniti band partition keys")?;
512 let (infinitiband_end_ports, tail) = SimpleArray::<InfinitiBandEndPort>::parse(tail)
513 .map_err(Into::<anyhow::Error>::into)
514 .context("parsing infiniti band end ports")?;
515 (Some(infinity_band_partition_keys), Some(infinitiband_end_ports), tail)
516 } else {
517 (None, None, tail)
518 };
519
520 let (generic_fs_contexts, tail) = CustomKeyHashedView::<GenericFsContext>::parse(tail)
521 .map_err(Into::<anyhow::Error>::into)
522 .context("parsing generic filesystem contexts")?;
523
524 let (range_transitions, tail) = SimpleArray::<RangeTransition>::parse(tail)
525 .map_err(Into::<anyhow::Error>::into)
526 .context("parsing range transitions")?;
527
528 let primary_names_count = new_policy.types().primary_names_count();
529 let mut attribute_maps = Vec::with_capacity(primary_names_count as usize);
530 let mut tail = tail;
531
532 for i in 0..primary_names_count {
533 let (item, next_tail) = ExtensibleBitmap::parse(tail)
534 .map_err(Into::<anyhow::Error>::into)
535 .with_context(|| format!("parsing {}th attribute map", i))?;
536 attribute_maps.push(item);
537 tail = next_tail;
538 }
539 let tail = tail;
540 let attribute_maps = attribute_maps;
541
542 let excess_bytes = rest_data.len() - tail.offset() as usize;
543
544 Ok((
545 ParsedPolicy {
546 data: rest_data,
547 new_policy: Arc::new(new_policy),
548
549 conditional_lists,
550 role_transitions,
551 role_allowlist,
552 filename_transition_list,
553 initial_sids,
554 filesystems,
555 ports,
556 network_interfaces,
557 nodes,
558 fs_uses,
559 ipv6_nodes,
560 infinitiband_partition_keys,
561 infinitiband_end_ports,
562 generic_fs_contexts,
563 range_transitions,
564 attribute_maps,
565 },
566 excess_bytes,
567 ))
568}
569
570impl ParsedPolicy {
571 pub fn validate(&self) -> Result<(), anyhow::Error> {
572 let need_init_sid = self.has_policycap(PolicyCap::UserspaceInitialContext);
573 let context = PolicyValidationContext {
574 data: self.data.clone(),
575 need_init_sid,
576 new_policy: self.new_policy.clone(),
577 };
578
579 self.conditional_lists
580 .validate(&context)
581 .map_err(Into::<anyhow::Error>::into)
582 .context("validating conditional_lists")?;
583 self.role_transitions
584 .validate(&context)
585 .map_err(Into::<anyhow::Error>::into)
586 .context("validating role_transitions")?;
587 self.role_allowlist
588 .validate(&context)
589 .map_err(Into::<anyhow::Error>::into)
590 .context("validating role_allowlist")?;
591 self.filename_transition_list
592 .validate(&context)
593 .map_err(Into::<anyhow::Error>::into)
594 .context("validating filename_transition_list")?;
595 self.initial_sids
596 .validate(&context)
597 .map_err(Into::<anyhow::Error>::into)
598 .context("validating initial_sids")?;
599 self.filesystems
600 .validate(&context)
601 .map_err(Into::<anyhow::Error>::into)
602 .context("validating filesystems")?;
603 self.ports
604 .validate(&context)
605 .map_err(Into::<anyhow::Error>::into)
606 .context("validating ports")?;
607 self.network_interfaces
608 .validate(&context)
609 .map_err(Into::<anyhow::Error>::into)
610 .context("validating network_interfaces")?;
611 self.nodes
612 .validate(&context)
613 .map_err(Into::<anyhow::Error>::into)
614 .context("validating nodes")?;
615 self.fs_uses
616 .validate(&context)
617 .map_err(Into::<anyhow::Error>::into)
618 .context("validating fs_uses")?;
619 self.ipv6_nodes
620 .validate(&context)
621 .map_err(Into::<anyhow::Error>::into)
622 .context("validating ipv6 nodes")?;
623 self.infinitiband_partition_keys
624 .validate(&context)
625 .map_err(Into::<anyhow::Error>::into)
626 .context("validating infinitiband_partition_keys")?;
627 self.infinitiband_end_ports
628 .validate(&context)
629 .map_err(Into::<anyhow::Error>::into)
630 .context("validating infinitiband_end_ports")?;
631 self.generic_fs_contexts
632 .validate(&context)
633 .map_err(Into::<anyhow::Error>::into)
634 .context("validating generic_fs_contexts")?;
635 self.range_transitions
636 .validate(&context)
637 .map_err(Into::<anyhow::Error>::into)
638 .context("validating range_transitions")?;
639 self.attribute_maps
640 .validate(&context)
641 .map_err(Into::<anyhow::Error>::into)
642 .context("validating attribute_maps")?;
643
644 let user_ids: HashSet<UserId> = self.new_policy.users().iter().map(|x| x.id()).collect();
646 let role_ids: HashSet<RoleId> = self.roles().iter().map(|x| x.id()).collect();
647 let class_ids: HashSet<ClassId> = self.classes().iter().map(|x| x.id()).collect();
648 let type_ids: HashSet<TypeId> = self.new_policy.types().iter().map(|t| t.id()).collect();
649 let sensitivity_ids: HashSet<SensitivityId> =
650 self.new_policy.sensitivities().iter().map(|x| x.id()).collect();
651 let category_ids: HashSet<CategoryId> =
652 self.new_policy.categories().iter().map(|x| x.id()).collect();
653
654 for initial_sid in &self.initial_sids.data {
658 self.validate_context(
659 initial_sid.context(),
660 &user_ids,
661 &role_ids,
662 &type_ids,
663 &sensitivity_ids,
664 &category_ids,
665 )?;
666 }
667
668 for fs_use in &self.fs_uses.data {
672 self.validate_context(
673 fs_use.context(),
674 &user_ids,
675 &role_ids,
676 &type_ids,
677 &sensitivity_ids,
678 &category_ids,
679 )?;
680 }
681
682 for entry in self.generic_fs_contexts.iter(&self.data) {
686 let entry = entry?;
687 for fs_context_view in entry.values().data().iter(&self.data) {
688 let fs_context = fs_context_view.parse(&self.data);
689 self.validate_context(
690 fs_context.context(),
691 &user_ids,
692 &role_ids,
693 &type_ids,
694 &sensitivity_ids,
695 &category_ids,
696 )?;
697 }
698 }
699
700 for transition in &self.role_transitions.data {
702 validate_id(&role_ids, transition.current_role(), "current_role")?;
703 validate_id(&type_ids, transition.type_(), "type")?;
704 validate_id(&class_ids, transition.class(), "class")?;
705 validate_id(&role_ids, transition.new_role(), "new_role")?;
706 }
707 for allow in &self.role_allowlist.data {
708 validate_id(&role_ids, allow.source_role(), "source_role")?;
709 validate_id(&role_ids, allow.new_role(), "new_role")?;
710 }
711
712 Ok(())
716 }
717}
718
719fn validate_id<IdType: Debug + Eq + Hash>(
720 id_set: &HashSet<IdType>,
721 id: IdType,
722 debug_kind: &'static str,
723) -> Result<(), anyhow::Error> {
724 if !id_set.contains(&id) {
725 return Err(ValidateError::UnknownId { kind: debug_kind, id: format!("{:?}", id) }.into());
726 }
727 Ok(())
728}