Skip to main content

selinux/policy/
parsed_policy.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::arrays::{
6    ACCESS_VECTOR_RULE_TYPE_ALLOW, ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM,
7    ACCESS_VECTOR_RULE_TYPE_AUDITALLOW, ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM,
8    ACCESS_VECTOR_RULE_TYPE_DONTAUDIT, ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM, AccessVectorRule,
9    AccessVectorRuleMetadata, ConditionalNode, Context, DeprecatedFilenameTransition,
10    ExtendedPermissions, FilenameTransition, FilenameTransitionList, FsUse, GenericFsContext,
11    IPv6Node, InfinitiBandEndPort, InfinitiBandPartitionKey, InitialSid,
12    MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY, NamedContextPair, Node, Port,
13    RangeTransition, RoleAllow, RoleAllows, RoleTransition, RoleTransitions, SimpleArray,
14    XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES, XPERMS_TYPE_IOCTL_PREFIXES, XPERMS_TYPE_NLMSG,
15    XpermsBitmap,
16};
17use super::error::{ParseError, ValidateError};
18use super::extensible_bitmap::ExtensibleBitmap;
19
20use super::constraints::evaluate_constraint;
21use super::parser::{PolicyCursor, PolicyData};
22use super::security_context::SecurityContext;
23use super::view::{Hashable, HashedArrayView};
24use super::{
25    AccessDecision, AccessVector, CategoryId, ClassId, MlsLevel, Parse, PolicyValidationContext,
26    RoleId, SELINUX_AVD_FLAGS_PERMISSIVE, SensitivityId, TypeId, UserId, Validate,
27    XpermsAccessDecision, XpermsKind,
28};
29
30use crate::new_policy::traits::{HasPolicyId, PolicyId};
31use crate::new_policy::{Class, NewPolicy};
32use crate::policy::arrays::FsContext;
33use crate::policy::view::CustomKeyHashedView;
34use crate::{NullessByteStr, PolicyCap};
35use std::ops::Deref;
36use std::sync::Arc;
37
38use anyhow::Context as _;
39use itertools::Itertools;
40use std::collections::HashSet;
41use std::fmt::Debug;
42use std::hash::Hash;
43use std::iter::Iterator;
44use zerocopy::little_endian as le;
45
46// As of 2026-01-30, more than five times larger than any policy seen in production or tests.
47const MAXIMUM_POLICY_SIZE: usize = 1 << 24;
48
49/// Parsed binary policy.
50#[derive(Debug)]
51pub struct ParsedPolicy {
52    /// Raw policy data (remaining).
53    data: PolicyData,
54
55    /// [`NewPolicy`] that handles the header and base tables.
56    new_policy: Arc<NewPolicy>,
57
58    /// The set of access vector rules referenced by this policy.
59    access_vector_rules: HashedArrayView<AccessVectorRule>,
60    conditional_lists: SimpleArray<ConditionalNode>,
61    /// The set of role transitions to apply when instantiating new objects.
62    role_transitions: RoleTransitions,
63    /// The set of role transitions allowed by policy.
64    role_allowlist: RoleAllows,
65    filename_transition_list: FilenameTransitionList,
66    initial_sids: SimpleArray<InitialSid>,
67    filesystems: SimpleArray<NamedContextPair>,
68    ports: SimpleArray<Port>,
69    network_interfaces: SimpleArray<NamedContextPair>,
70    nodes: SimpleArray<Node>,
71    fs_uses: SimpleArray<FsUse>,
72    ipv6_nodes: SimpleArray<IPv6Node>,
73    infinitiband_partition_keys: Option<SimpleArray<InfinitiBandPartitionKey>>,
74    infinitiband_end_ports: Option<SimpleArray<InfinitiBandEndPort>>,
75    /// A set of labeling statements to apply to given filesystems and/or their subdirectories.
76    /// Corresponds to the `genfscon` labeling statement in the policy.
77    generic_fs_contexts: CustomKeyHashedView<GenericFsContext>,
78    range_transitions: SimpleArray<RangeTransition>,
79    /// Extensible bitmaps that encode associations between types and attributes.
80    attribute_maps: Vec<ExtensibleBitmap>,
81}
82
83impl Deref for ParsedPolicy {
84    type Target = NewPolicy;
85    fn deref(&self) -> &Self::Target {
86        &self.new_policy
87    }
88}
89
90impl ParsedPolicy {
91    /// Returns true if the specified capability is in the policy's enabled capabilities set.
92    pub fn has_policycap(&self, policy_cap: PolicyCap) -> bool {
93        self.new_policy.policy_capabilities().is_set(policy_cap as u32)
94    }
95
96    /// Computes the access granted to `source_type` on `target_type`, for the specified
97    /// `target_class`. The result is a set of access vectors with bits set for each
98    /// `target_class` permission, describing which permissions are allowed, and
99    /// which should have access checks audit-logged when denied, or allowed.
100    ///
101    /// An [`AccessDecision`] is accumulated, starting from no permissions to be granted,
102    /// nor audit-logged if allowed, and all permissions to be audit-logged if denied.
103    /// Permissions that are explicitly `allow`ed, but that are subject to unsatisfied
104    /// constraints, are removed from the allowed set. Matching policy statements then
105    /// add permissions to the granted & audit-allow sets, or remove them from the
106    /// audit-deny set.
107    pub(super) fn compute_access_decision(
108        &self,
109        source_context: &SecurityContext,
110        target_context: &SecurityContext,
111        target_class: &Class,
112    ) -> AccessDecision {
113        let mut access_decision = self.compute_explicitly_allowed(
114            source_context.type_(),
115            target_context.type_(),
116            target_class,
117        );
118        access_decision.allow -=
119            self.compute_denied_by_constraints(source_context, target_context, target_class);
120        access_decision
121    }
122
123    /// Computes the access granted to `source_type` on `target_type`, for the specified
124    /// `target_class`. The result is a set of access vectors with bits set for each
125    /// `target_class` permission, describing which permissions are explicitly allowed,
126    /// and which should have access checks audit-logged when denied, or allowed.
127    pub(super) fn compute_explicitly_allowed(
128        &self,
129        source_type: TypeId,
130        target_type: TypeId,
131        target_class: &Class,
132    ) -> AccessDecision {
133        let target_class_id = target_class.id();
134
135        let mut computed_access_vector = AccessVector::NONE;
136        let mut computed_audit_allow = AccessVector::NONE;
137        let mut computed_audit_deny = AccessVector::ALL;
138
139        let source_attribute_bitmap: &ExtensibleBitmap =
140            &self.attribute_maps[(source_type.as_u32() - 1) as usize];
141        let target_attribute_bitmap: &ExtensibleBitmap =
142            &self.attribute_maps[(target_type.as_u32() - 1) as usize];
143
144        for (source_bit_index, target_bit_index) in Itertools::cartesian_product(
145            source_attribute_bitmap.indices_of_set_bits(),
146            target_attribute_bitmap.indices_of_set_bits(),
147        ) {
148            let source_id = TypeId::from_u32((source_bit_index + 1) as u32).unwrap();
149            let target_id = TypeId::from_u32((target_bit_index + 1) as u32).unwrap();
150
151            if let Some(allow_rule) = self.access_vector_rules_find(
152                source_id,
153                target_id,
154                target_class_id,
155                ACCESS_VECTOR_RULE_TYPE_ALLOW,
156            ) {
157                // `access_vector` has bits set for each permission allowed by this rule.
158                computed_access_vector |= allow_rule.access_vector().unwrap();
159            }
160            if let Some(auditallow_rule) = self.access_vector_rules_find(
161                source_id,
162                target_id,
163                target_class_id,
164                ACCESS_VECTOR_RULE_TYPE_AUDITALLOW,
165            ) {
166                // `access_vector` has bits set for each permission to audit when allowed.
167                computed_audit_allow |= auditallow_rule.access_vector().unwrap();
168            }
169            if let Some(dontaudit_rule) = self.access_vector_rules_find(
170                source_id,
171                target_id,
172                target_class_id,
173                ACCESS_VECTOR_RULE_TYPE_DONTAUDIT,
174            ) {
175                // `access_vector` has bits cleared for each permission not to audit on denial.
176                computed_audit_deny &= dontaudit_rule.access_vector().unwrap();
177            }
178        }
179
180        // If the `source_type` is bounded by some `parent_type` then bound the allowed permissions
181        // to those available to the parent. Doing the calculation here ensures that type-bounds
182        // take into account bounding ancestors, if any.
183        if let Some(parent) = self.types().get_by_id(source_type).unwrap().bounded_by() {
184            // If `source_type`==`target_type` then this is a "self" permission check, which should
185            // be bounded to the parent domain's "self" permissions.
186            let access = if source_type == target_type {
187                self.compute_explicitly_allowed(parent, parent, target_class)
188            } else {
189                self.compute_explicitly_allowed(parent, target_type, target_class)
190            };
191            computed_access_vector &= access.allow;
192        }
193
194        let mut flags = 0;
195        if self.permissive_map().contains(source_type) {
196            flags |= SELINUX_AVD_FLAGS_PERMISSIVE;
197        }
198        AccessDecision {
199            allow: computed_access_vector,
200            auditallow: computed_audit_allow,
201            auditdeny: computed_audit_deny,
202            flags,
203            todo_bug: None,
204        }
205    }
206
207    /// A permission is denied if it matches at least one unsatisfied constraint.
208    fn compute_denied_by_constraints(
209        &self,
210        source_context: &SecurityContext,
211        target_context: &SecurityContext,
212        target_class: &Class,
213    ) -> AccessVector {
214        let mut denied = AccessVector::NONE;
215        for constraint in target_class.constraints() {
216            if !evaluate_constraint(constraint.constraint_expr(), source_context, target_context) {
217                denied |= constraint.access_vector();
218            }
219        }
220        denied
221    }
222
223    /// Computes the access decision for set of extended permissions of a given kind and with a
224    /// given prefix byte, for a particular source and target context and target class.
225    pub(super) fn compute_xperms_access_decision(
226        &self,
227        xperms_kind: XpermsKind,
228        source_context: &SecurityContext,
229        target_context: &SecurityContext,
230        target_class: &Class,
231        xperms_prefix: u8,
232    ) -> XpermsAccessDecision {
233        let target_class_id = target_class.id();
234
235        let mut explicit_allow: Option<XpermsBitmap> = None;
236        let mut auditallow = XpermsBitmap::NONE;
237        let mut auditdeny = XpermsBitmap::ALL;
238
239        let xperms_types = match xperms_kind {
240            XpermsKind::Ioctl => {
241                [XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES, XPERMS_TYPE_IOCTL_PREFIXES].as_slice()
242            }
243            XpermsKind::Nlmsg => [XPERMS_TYPE_NLMSG].as_slice(),
244        };
245        let bitmap_if_prefix_matches =
246            |xperms_prefix: u8, xperms: &ExtendedPermissions| match xperms_kind {
247                XpermsKind::Ioctl => match xperms.xperms_type {
248                    XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES => (xperms.xperms_optional_prefix
249                        == xperms_prefix)
250                        .then_some(xperms.xperms_bitmap),
251                    XPERMS_TYPE_IOCTL_PREFIXES => {
252                        xperms.xperms_bitmap.contains(xperms_prefix).then_some(XpermsBitmap::ALL)
253                    }
254                    _ => None,
255                },
256                XpermsKind::Nlmsg => match xperms.xperms_type {
257                    XPERMS_TYPE_NLMSG => (xperms.xperms_optional_prefix == xperms_prefix)
258                        .then_some(xperms.xperms_bitmap),
259                    _ => None,
260                },
261            };
262
263        let source_attribute_bitmap: &ExtensibleBitmap =
264            &self.attribute_maps[(source_context.type_().as_u32() - 1) as usize];
265        let target_attribute_bitmap: &ExtensibleBitmap =
266            &self.attribute_maps[(target_context.type_().as_u32() - 1) as usize];
267
268        for (source_bit_index, target_bit_index) in Itertools::cartesian_product(
269            source_attribute_bitmap.indices_of_set_bits(),
270            target_attribute_bitmap.indices_of_set_bits(),
271        ) {
272            let source_id = TypeId::from_u32((source_bit_index + 1) as u32).unwrap();
273            let target_id = TypeId::from_u32((target_bit_index + 1) as u32).unwrap();
274
275            for xperms_allow_rule in self.access_vector_rules_find_all(
276                source_id,
277                target_id,
278                target_class_id,
279                ACCESS_VECTOR_RULE_TYPE_ALLOWXPERM,
280            ) {
281                let xperms = xperms_allow_rule.extended_permissions().unwrap();
282
283                // Only filter xperms if there is at least one `allowxperm` rule for the relevant
284                // kind of extended permission. If this condition is not satisfied by any
285                // access vector rule, then all xperms of the relevant type are allowed.
286                if xperms_types.contains(&xperms.xperms_type) {
287                    explicit_allow.get_or_insert(XpermsBitmap::NONE);
288                }
289
290                if let Some(ref xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
291                    (*explicit_allow.get_or_insert(XpermsBitmap::NONE)) |= xperms_bitmap;
292                }
293            }
294
295            for xperms_auditallow_rule in self.access_vector_rules_find_all(
296                source_id,
297                target_id,
298                target_class_id,
299                ACCESS_VECTOR_RULE_TYPE_AUDITALLOWXPERM,
300            ) {
301                let xperms = xperms_auditallow_rule.extended_permissions().unwrap();
302                if let Some(ref xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
303                    auditallow |= xperms_bitmap;
304                }
305            }
306
307            for xperms_dontaudit_rule in self.access_vector_rules_find_all(
308                source_id,
309                target_id,
310                target_class_id,
311                ACCESS_VECTOR_RULE_TYPE_DONTAUDITXPERM,
312            ) {
313                let xperms = xperms_dontaudit_rule.extended_permissions().unwrap();
314                if let Some(ref xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
315                    auditdeny -= xperms_bitmap;
316                }
317            }
318        }
319        let allow = explicit_allow.unwrap_or(XpermsBitmap::ALL);
320        XpermsAccessDecision { allow, auditallow, auditdeny }
321    }
322
323    /// Returns the policy entry for the specified initial Security Context.
324    pub(super) fn initial_context(&self, mut id: crate::InitialSid) -> &Context {
325        // If "userspace_initial_context" is not set then the "init" SID is treated as "kernel".
326        if id == crate::InitialSid::Init && !self.has_policycap(PolicyCap::UserspaceInitialContext)
327        {
328            id = crate::InitialSid::Kernel
329        }
330
331        // [`InitialSids`] validates that all `InitialSid` values are defined by the policy.
332        let id = le::U32::from(id as u32);
333        &self.initial_sids.data.iter().find(|initial| initial.id() == id).unwrap().context()
334    }
335
336    pub(super) fn fs_uses(&self) -> &[FsUse] {
337        &self.fs_uses.data
338    }
339
340    pub(super) fn genfscon_find_all(&self, fs_type: &str) -> impl Iterator<Item = FsContext> {
341        let query = GenericFsContext::for_query(fs_type);
342        self.generic_fs_contexts.find_all(query, &self.data)
343    }
344
345    pub(super) fn role_allowlist(&self) -> &[RoleAllow] {
346        &self.role_allowlist.data
347    }
348
349    pub(super) fn role_transitions(&self) -> &[RoleTransition] {
350        &self.role_transitions.data
351    }
352
353    pub(super) fn range_transitions(&self) -> &[RangeTransition] {
354        &self.range_transitions.data
355    }
356
357    pub(super) fn access_vector_rules_find(
358        &self,
359        source: TypeId,
360        target: TypeId,
361        class: ClassId,
362        rule_type: u16,
363    ) -> Option<AccessVectorRule> {
364        let query = AccessVectorRuleMetadata::for_query(source, target, class, rule_type);
365        self.access_vector_rules.find(query, &self.data)
366    }
367
368    pub(super) fn access_vector_rules_find_all(
369        &self,
370        source: TypeId,
371        target: TypeId,
372        class: ClassId,
373        rule_type: u16,
374    ) -> impl Iterator<Item = AccessVectorRule> {
375        let query = AccessVectorRuleMetadata::for_query(source, target, class, rule_type);
376        self.access_vector_rules.find_all(query, &self.data)
377    }
378
379    #[cfg(test)]
380    pub(super) fn access_vector_rules_for_test(
381        &self,
382    ) -> impl Iterator<Item = AccessVectorRule> + use<'_> {
383        use super::arrays::testing::access_vector_rule_ordering;
384        use itertools::Itertools;
385
386        self.access_vector_rules
387            .iter(&self.data)
388            .map(|view| view.parse(&self.data))
389            .sorted_by(access_vector_rule_ordering)
390    }
391
392    pub(super) fn compute_filename_transition(
393        &self,
394        source_type: TypeId,
395        target_type: TypeId,
396        class: ClassId,
397        name: NullessByteStr<'_>,
398    ) -> Option<TypeId> {
399        match &self.filename_transition_list {
400            FilenameTransitionList::PolicyVersionGeq33(list) => {
401                let entry = list.data.iter().find(|transition| {
402                    transition.target_type() == target_type
403                        && transition.target_class() == class
404                        && transition.name_bytes() == name.as_bytes()
405                })?;
406                entry
407                    .outputs()
408                    .iter()
409                    .find(|entry| entry.has_source_type(source_type))
410                    .map(|x| x.out_type())
411            }
412            FilenameTransitionList::PolicyVersionLeq32(list) => list
413                .data
414                .iter()
415                .find(|transition| {
416                    transition.target_class() == class
417                        && transition.target_type() == target_type
418                        && transition.source_type() == source_type
419                        && transition.name_bytes() == name.as_bytes()
420                })
421                .map(|x| x.out_type()),
422        }
423    }
424
425    // Validate that all sensitivity and category IDs referenced in the MLS level are
426    // defined.
427    fn validate_mls_level(
428        &self,
429        level: &MlsLevel,
430        sensitivity_ids: &HashSet<SensitivityId>,
431        category_ids: &HashSet<CategoryId>,
432    ) -> Result<(), anyhow::Error> {
433        validate_id(sensitivity_ids, level.sensitivity(), "sensitivity")?;
434        for id in level.category_ids() {
435            validate_id(category_ids, id, "category")?;
436        }
437        Ok(())
438    }
439
440    // Validate an MLS range statement against sets of defined sensitivity and category
441    // IDs:
442    // - Verify that all sensitivity and category IDs referenced in the MLS levels are
443    //   defined.
444    // - Verify that the range is internally consistent; i.e., the high level (if any)
445    //   dominates the low level.
446    fn validate_mls_range(
447        &self,
448        low_level: &MlsLevel,
449        high_level: &Option<MlsLevel>,
450        sensitivity_ids: &HashSet<SensitivityId>,
451        category_ids: &HashSet<CategoryId>,
452    ) -> Result<(), anyhow::Error> {
453        self.validate_mls_level(low_level, sensitivity_ids, category_ids)?;
454        if let Some(high) = high_level {
455            self.validate_mls_level(high, sensitivity_ids, category_ids)?;
456            if !high.dominates(low_level) {
457                return Err(ValidateError::InvalidMlsRange {
458                    low: low_level.to_string(self).into(),
459                    high: high.to_string(self).into(),
460                }
461                .into());
462            }
463        }
464        Ok(())
465    }
466
467    fn validate_context(
468        &self,
469        context: &Context,
470        user_ids: &HashSet<UserId>,
471        role_ids: &HashSet<RoleId>,
472        type_ids: &HashSet<TypeId>,
473        sensitivity_ids: &HashSet<SensitivityId>,
474        category_ids: &HashSet<CategoryId>,
475    ) -> Result<(), anyhow::Error> {
476        validate_id(user_ids, context.user_id(), "user")?;
477        validate_id(role_ids, context.role_id(), "role")?;
478        validate_id(type_ids, context.type_id(), "type")?;
479        self.validate_mls_range(
480            context.low_level(),
481            context.high_level(),
482            sensitivity_ids,
483            category_ids,
484        )?;
485        Ok(())
486    }
487}
488
489impl ParsedPolicy {
490    /// Parses the binary policy stored in `bytes`. It is an error for `bytes` to have trailing
491    /// bytes after policy parsing completes.
492    pub(super) fn parse(data: PolicyData) -> Result<Self, anyhow::Error> {
493        let policy_size = data.len();
494        if MAXIMUM_POLICY_SIZE <= policy_size {
495            return Err(anyhow::Error::from(ParseError::UnsupportedlyLarge {
496                observed: policy_size,
497                limit: MAXIMUM_POLICY_SIZE,
498            }));
499        }
500        let new_policy =
501            NewPolicy::parse(&data).map_err(|e| anyhow::anyhow!("new parser failed: {:?}", e))?;
502        new_policy.validate().context("validating new policy structure")?;
503
504        let rest_data = new_policy.rest_bytes();
505        let (policy, excess_bytes) = parse_policy_remaining(new_policy, rest_data)?;
506        if excess_bytes > 0 {
507            return Err(anyhow::Error::from(ParseError::TrailingBytes { num_bytes: excess_bytes }));
508        }
509        Ok(policy)
510    }
511}
512
513/// Parses the remaining parts of the policy from `rest_data` to construct a [`ParsedPolicy`].
514fn parse_policy_remaining(
515    new_policy: NewPolicy,
516    rest_data: PolicyData,
517) -> Result<(ParsedPolicy, usize), anyhow::Error> {
518    let tail = PolicyCursor::new(&rest_data);
519
520    let (access_vector_rules, tail) = HashedArrayView::<AccessVectorRule>::parse(tail)
521        .map_err(Into::<anyhow::Error>::into)
522        .context("parsing access vector rules")?;
523
524    let (conditional_lists, tail) = SimpleArray::<ConditionalNode>::parse(tail)
525        .map_err(Into::<anyhow::Error>::into)
526        .context("parsing conditional lists")?;
527
528    let (role_transitions, tail) = RoleTransitions::parse(tail)
529        .map_err(Into::<anyhow::Error>::into)
530        .context("parsing role transitions")?;
531
532    let (role_allowlist, tail) = RoleAllows::parse(tail)
533        .map_err(Into::<anyhow::Error>::into)
534        .context("parsing role allow rules")?;
535
536    let (filename_transition_list, tail) = if new_policy.policy_version() >= 33 {
537        let (filename_transition_list, tail) = SimpleArray::<FilenameTransition>::parse(tail)
538            .map_err(Into::<anyhow::Error>::into)
539            .context("parsing standard filename transitions")?;
540        (FilenameTransitionList::PolicyVersionGeq33(filename_transition_list), tail)
541    } else {
542        let (filename_transition_list, tail) =
543            SimpleArray::<DeprecatedFilenameTransition>::parse(tail)
544                .map_err(Into::<anyhow::Error>::into)
545                .context("parsing deprecated filename transitions")?;
546        (FilenameTransitionList::PolicyVersionLeq32(filename_transition_list), tail)
547    };
548
549    let (initial_sids, tail) = SimpleArray::<InitialSid>::parse(tail)
550        .map_err(Into::<anyhow::Error>::into)
551        .context("parsing initial sids")?;
552
553    let (filesystems, tail) = SimpleArray::<NamedContextPair>::parse(tail)
554        .map_err(Into::<anyhow::Error>::into)
555        .context("parsing filesystem contexts")?;
556
557    let (ports, tail) = SimpleArray::<Port>::parse(tail)
558        .map_err(Into::<anyhow::Error>::into)
559        .context("parsing ports")?;
560
561    let (network_interfaces, tail) = SimpleArray::<NamedContextPair>::parse(tail)
562        .map_err(Into::<anyhow::Error>::into)
563        .context("parsing network interfaces")?;
564
565    let (nodes, tail) = SimpleArray::<Node>::parse(tail)
566        .map_err(Into::<anyhow::Error>::into)
567        .context("parsing nodes")?;
568
569    let (fs_uses, tail) = SimpleArray::<FsUse>::parse(tail)
570        .map_err(Into::<anyhow::Error>::into)
571        .context("parsing fs uses")?;
572
573    let (ipv6_nodes, tail) = SimpleArray::<IPv6Node>::parse(tail)
574        .map_err(Into::<anyhow::Error>::into)
575        .context("parsing ipv6 nodes")?;
576
577    let (infinitiband_partition_keys, infinitiband_end_ports, tail) =
578        if new_policy.policy_version() >= MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY {
579            let (infinity_band_partition_keys, tail) =
580                SimpleArray::<InfinitiBandPartitionKey>::parse(tail)
581                    .map_err(Into::<anyhow::Error>::into)
582                    .context("parsing infiniti band partition keys")?;
583            let (infinitiband_end_ports, tail) = SimpleArray::<InfinitiBandEndPort>::parse(tail)
584                .map_err(Into::<anyhow::Error>::into)
585                .context("parsing infiniti band end ports")?;
586            (Some(infinity_band_partition_keys), Some(infinitiband_end_ports), tail)
587        } else {
588            (None, None, tail)
589        };
590
591    let (generic_fs_contexts, tail) = CustomKeyHashedView::<GenericFsContext>::parse(tail)
592        .map_err(Into::<anyhow::Error>::into)
593        .context("parsing generic filesystem contexts")?;
594
595    let (range_transitions, tail) = SimpleArray::<RangeTransition>::parse(tail)
596        .map_err(Into::<anyhow::Error>::into)
597        .context("parsing range transitions")?;
598
599    let primary_names_count = new_policy.types().primary_names_count();
600    let mut attribute_maps = Vec::with_capacity(primary_names_count as usize);
601    let mut tail = tail;
602
603    for i in 0..primary_names_count {
604        let (item, next_tail) = ExtensibleBitmap::parse(tail)
605            .map_err(Into::<anyhow::Error>::into)
606            .with_context(|| format!("parsing {}th attribute map", i))?;
607        attribute_maps.push(item);
608        tail = next_tail;
609    }
610    let tail = tail;
611    let attribute_maps = attribute_maps;
612
613    let excess_bytes = rest_data.len() - tail.offset() as usize;
614
615    Ok((
616        ParsedPolicy {
617            data: rest_data,
618            new_policy: Arc::new(new_policy),
619
620            access_vector_rules,
621            conditional_lists,
622            role_transitions,
623            role_allowlist,
624            filename_transition_list,
625            initial_sids,
626            filesystems,
627            ports,
628            network_interfaces,
629            nodes,
630            fs_uses,
631            ipv6_nodes,
632            infinitiband_partition_keys,
633            infinitiband_end_ports,
634            generic_fs_contexts,
635            range_transitions,
636            attribute_maps,
637        },
638        excess_bytes,
639    ))
640}
641
642impl ParsedPolicy {
643    pub fn validate(&self) -> Result<(), anyhow::Error> {
644        let need_init_sid = self.has_policycap(PolicyCap::UserspaceInitialContext);
645        let context = PolicyValidationContext {
646            data: self.data.clone(),
647            need_init_sid,
648            new_policy: self.new_policy.clone(),
649        };
650
651        self.access_vector_rules
652            .validate(&context)
653            .map_err(Into::<anyhow::Error>::into)
654            .context("validating access_vector_rules")?;
655        self.conditional_lists
656            .validate(&context)
657            .map_err(Into::<anyhow::Error>::into)
658            .context("validating conditional_lists")?;
659        self.role_transitions
660            .validate(&context)
661            .map_err(Into::<anyhow::Error>::into)
662            .context("validating role_transitions")?;
663        self.role_allowlist
664            .validate(&context)
665            .map_err(Into::<anyhow::Error>::into)
666            .context("validating role_allowlist")?;
667        self.filename_transition_list
668            .validate(&context)
669            .map_err(Into::<anyhow::Error>::into)
670            .context("validating filename_transition_list")?;
671        self.initial_sids
672            .validate(&context)
673            .map_err(Into::<anyhow::Error>::into)
674            .context("validating initial_sids")?;
675        self.filesystems
676            .validate(&context)
677            .map_err(Into::<anyhow::Error>::into)
678            .context("validating filesystems")?;
679        self.ports
680            .validate(&context)
681            .map_err(Into::<anyhow::Error>::into)
682            .context("validating ports")?;
683        self.network_interfaces
684            .validate(&context)
685            .map_err(Into::<anyhow::Error>::into)
686            .context("validating network_interfaces")?;
687        self.nodes
688            .validate(&context)
689            .map_err(Into::<anyhow::Error>::into)
690            .context("validating nodes")?;
691        self.fs_uses
692            .validate(&context)
693            .map_err(Into::<anyhow::Error>::into)
694            .context("validating fs_uses")?;
695        self.ipv6_nodes
696            .validate(&context)
697            .map_err(Into::<anyhow::Error>::into)
698            .context("validating ipv6 nodes")?;
699        self.infinitiband_partition_keys
700            .validate(&context)
701            .map_err(Into::<anyhow::Error>::into)
702            .context("validating infinitiband_partition_keys")?;
703        self.infinitiband_end_ports
704            .validate(&context)
705            .map_err(Into::<anyhow::Error>::into)
706            .context("validating infinitiband_end_ports")?;
707        self.generic_fs_contexts
708            .validate(&context)
709            .map_err(Into::<anyhow::Error>::into)
710            .context("validating generic_fs_contexts")?;
711        self.range_transitions
712            .validate(&context)
713            .map_err(Into::<anyhow::Error>::into)
714            .context("validating range_transitions")?;
715        self.attribute_maps
716            .validate(&context)
717            .map_err(Into::<anyhow::Error>::into)
718            .context("validating attribute_maps")?;
719
720        // Collate the sets of user, role, type, sensitivity and category Ids.
721        let user_ids: HashSet<UserId> = self.new_policy.users().iter().map(|x| x.id()).collect();
722        let role_ids: HashSet<RoleId> = self.roles().iter().map(|x| x.id()).collect();
723        let class_ids: HashSet<ClassId> = self.classes().iter().map(|x| x.id()).collect();
724        let type_ids: HashSet<TypeId> = self.new_policy.types().iter().map(|t| t.id()).collect();
725        let sensitivity_ids: HashSet<SensitivityId> =
726            self.new_policy.sensitivities().iter().map(|x| x.id()).collect();
727        let category_ids: HashSet<CategoryId> =
728            self.new_policy.categories().iter().map(|x| x.id()).collect();
729
730        // Validate that initial contexts use only defined user, role, type, etc Ids.
731        // Check that all sensitivity and category IDs are defined and that MLS levels
732        // are internally consistent.
733        for initial_sid in &self.initial_sids.data {
734            self.validate_context(
735                initial_sid.context(),
736                &user_ids,
737                &role_ids,
738                &type_ids,
739                &sensitivity_ids,
740                &category_ids,
741            )?;
742        }
743
744        // Validate that contexts specified in filesystem labeling rules only use
745        // policy-defined Ids for their fields. Check that MLS levels are internally
746        // consistent.
747        for fs_use in &self.fs_uses.data {
748            self.validate_context(
749                fs_use.context(),
750                &user_ids,
751                &role_ids,
752                &type_ids,
753                &sensitivity_ids,
754                &category_ids,
755            )?;
756        }
757
758        // Validate that contexts specified in genfscon rules only use
759        // policy-defined Ids for their fields. Check that MLS levels are internally
760        // consistent.
761        for entry in self.generic_fs_contexts.iter(&self.data) {
762            let entry = entry?;
763            for fs_context_view in entry.values().data().iter(&self.data) {
764                let fs_context = fs_context_view.parse(&self.data);
765                self.validate_context(
766                    fs_context.context(),
767                    &user_ids,
768                    &role_ids,
769                    &type_ids,
770                    &sensitivity_ids,
771                    &category_ids,
772                )?;
773            }
774        }
775
776        // Validate that roles output by role- transitions & allows are defined.
777        for transition in &self.role_transitions.data {
778            validate_id(&role_ids, transition.current_role(), "current_role")?;
779            validate_id(&type_ids, transition.type_(), "type")?;
780            validate_id(&class_ids, transition.class(), "class")?;
781            validate_id(&role_ids, transition.new_role(), "new_role")?;
782        }
783        for allow in &self.role_allowlist.data {
784            validate_id(&role_ids, allow.source_role(), "source_role")?;
785            validate_id(&role_ids, allow.new_role(), "new_role")?;
786        }
787
788        // Validate that types output by access vector rules are defined.
789        for access_vector_rule_view in self.access_vector_rules.iter(&self.data) {
790            let access_vector_rule = access_vector_rule_view.parse(&self.data);
791            if let Some(type_id) = access_vector_rule.new_type() {
792                validate_id(&type_ids, type_id, "new_type")?;
793            }
794        }
795
796        // To-do comments for cross-policy validations yet to be implemented go here.
797        // TODO(b/356569876): Determine which "bounds" should be verified for correctness here.
798
799        Ok(())
800    }
801}
802
803fn validate_id<IdType: Debug + Eq + Hash>(
804    id_set: &HashSet<IdType>,
805    id: IdType,
806    debug_kind: &'static str,
807) -> Result<(), anyhow::Error> {
808    if !id_set.contains(&id) {
809        return Err(ValidateError::UnknownId { kind: debug_kind, id: format!("{:?}", id) }.into());
810    }
811    Ok(())
812}