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