Skip to main content

selinux/new_policy/
context.rs

1// Copyright 2026 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 std::cmp::Ordering;
6
7pub use selinux_policy_derive::{Parse, Serialize, Validate};
8
9use super::error::{ParseError, SerializeError, ValidateError};
10use super::parser::PolicyCursor;
11use super::traits::{Parse, Serialize, Validate};
12use super::{CategoryId, CategorySet, RoleId, SensitivityId, TypeId, UserId};
13
14/// Security level in MLS (Multi-Level Security), consisting of a sensitivity and a set of categories.
15#[derive(Debug, Clone, PartialEq, Eq, Parse, Serialize, Validate)]
16pub struct MlsLevel {
17    sensitivity: SensitivityId,
18    categories: CategorySet,
19}
20
21impl MlsLevel {
22    /// Constructs a new [`MlsLevel`].
23    pub fn new(sensitivity: SensitivityId, categories: CategorySet) -> Self {
24        Self { sensitivity, categories }
25    }
26
27    /// Returns the sensitivity level.
28    pub fn sensitivity(&self) -> SensitivityId {
29        self.sensitivity
30    }
31
32    /// Returns the set of categories.
33    pub fn categories(&self) -> &CategorySet {
34        &self.categories
35    }
36
37    /// Returns an iterator over the category IDs.
38    pub fn category_ids(&self) -> impl Iterator<Item = CategoryId> + use<'_> {
39        self.categories.iter()
40    }
41
42    /// Compares two [`MlsLevel`]s. Returns [`None`] if they are incomparable.
43    pub fn compare(&self, other: &Self) -> Option<Ordering> {
44        let s_order = self.sensitivity().cmp(&other.sensitivity());
45        let c_order = self.categories().compare(other.categories())?;
46        if s_order == c_order {
47            return Some(s_order);
48        } else if c_order == Ordering::Equal {
49            return Some(s_order);
50        } else if s_order == Ordering::Equal {
51            return Some(c_order);
52        }
53        None
54    }
55
56    /// Returns `true` if `self` dominates `other` (i.e. is greater than or equal to).
57    pub fn dominates(&self, other: &Self) -> bool {
58        self.sensitivity() >= other.sensitivity()
59            && self.categories().is_superset(other.categories())
60    }
61}
62
63/// Security range in MLS, consisting of a low level and an optional high level.
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct MlsRange {
66    low: MlsLevel,
67    high: Option<MlsLevel>,
68}
69
70impl Parse for MlsRange {
71    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
72        let levels_count = u32::parse(cursor)?;
73        let sensitivity_low = SensitivityId::parse(cursor)?;
74
75        // If levels_count > 1, the MLS range contains both low and high security levels.
76        if levels_count > 1 {
77            let sensitivity_high = SensitivityId::parse(cursor)?;
78            let low_categories = CategorySet::parse(cursor)?;
79            let high_categories = CategorySet::parse(cursor)?;
80
81            Ok(Self {
82                low: MlsLevel { sensitivity: sensitivity_low, categories: low_categories },
83                high: Some(MlsLevel { sensitivity: sensitivity_high, categories: high_categories }),
84            })
85        } else {
86            let low_categories = CategorySet::parse(cursor)?;
87            Ok(Self {
88                low: MlsLevel { sensitivity: sensitivity_low, categories: low_categories },
89                high: None,
90            })
91        }
92    }
93}
94
95impl Serialize for MlsRange {
96    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
97        if let Some(ref high) = self.high {
98            2u32.serialize(writer)?;
99            self.low.sensitivity.serialize(writer)?;
100            high.sensitivity.serialize(writer)?;
101            self.low.categories.serialize(writer)?;
102            high.categories.serialize(writer)?;
103        } else {
104            1u32.serialize(writer)?;
105            self.low.sensitivity.serialize(writer)?;
106            self.low.categories.serialize(writer)?;
107        }
108        Ok(())
109    }
110}
111
112impl Validate for MlsRange {
113    fn validate(&self, policy: &super::NewPolicy) -> Result<(), ValidateError> {
114        self.low.validate(policy)?;
115        if let Some(ref high) = self.high {
116            high.validate(policy)?;
117            if !high.dominates(&self.low) {
118                return Err(ValidateError::InvalidMlsRange);
119            }
120        }
121        Ok(())
122    }
123}
124
125impl MlsRange {
126    /// Constructs a new [`MlsRange`].
127    pub fn new(low: MlsLevel, high: Option<MlsLevel>) -> Self {
128        Self { low, high }
129    }
130
131    /// Returns the low security level.
132    pub fn low(&self) -> &MlsLevel {
133        &self.low
134    }
135
136    /// Returns the high security level, if present.
137    pub fn high(&self) -> &Option<MlsLevel> {
138        &self.high
139    }
140}
141
142/// Security context containing user, role, type, and MLS range.
143#[derive(Debug, Clone, PartialEq, Eq, Parse, Serialize, Validate)]
144pub struct Context {
145    user: UserId,
146    role: RoleId,
147    context_type: TypeId,
148    mls_range: MlsRange,
149}
150
151impl Context {
152    /// Constructs a new [`Context`].
153    pub fn new(user: UserId, role: RoleId, context_type: TypeId, mls_range: MlsRange) -> Self {
154        Self { user, role, context_type, mls_range }
155    }
156
157    /// Returns the user ID.
158    pub fn user_id(&self) -> UserId {
159        self.user
160    }
161
162    /// Returns the role ID.
163    pub fn role_id(&self) -> RoleId {
164        self.role
165    }
166
167    /// Returns the type ID.
168    pub fn type_id(&self) -> TypeId {
169        self.context_type
170    }
171
172    /// Returns the low security level.
173    pub fn low_level(&self) -> &MlsLevel {
174        &self.mls_range.low
175    }
176
177    /// Returns the high security level, if present.
178    pub fn high_level(&self) -> &Option<MlsLevel> {
179        &self.mls_range.high
180    }
181}