Skip to main content

selinux/policy/
classes.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::num::NonZeroU16;
6
7use fuchsia_rcu::RcuDroppable;
8use selinux_policy_derive::{HasName, HasPolicyId, Parse, Serialize, Validate};
9
10use super::Policy;
11use super::constraints::{Constraint, ConstraintTerm};
12use super::error::{ParseError, SerializeError, ValidateError};
13use super::id_type::IdType;
14use super::indexed::IdAndNameIndexed;
15use super::parser::{Array, PolicyCursor, PolicyWriter};
16use super::permissions::Permission;
17use super::traits::{Parse, PolicyId, Serialize, Validate};
18
19/// Tag type for type safety of policy class identifiers.
20#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, RcuDroppable)]
21pub struct ClassTag;
22
23/// Identifies a class within a policy.
24pub type ClassId = IdType<NonZeroU16, ClassTag>;
25
26#[derive(Parse, Serialize)]
27struct BinaryClassMetadata {
28    key_length: u32,
29    common_key_length: u32,
30    id: u32,
31    /// Included in the policy to allow allocation of index structures to be optimized.
32    permission_primary_names_count: u32,
33    permission_count: u32,
34    constraint_count: u32,
35}
36
37/// Rule for computing default user, role, or type when creating an object of a class.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Parse, Serialize, Validate)]
39#[policy(wire_type = u32)]
40pub enum ClassDefault {
41    Unspecified = 0,
42    Source = 1,
43    Target = 2,
44}
45
46/// Rule for computing default MLS range when creating an object of a class.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Parse, Serialize, Validate)]
48#[policy(wire_type = u32)]
49pub enum ClassDefaultRange {
50    Unspecified = 0,
51    SourceLow = 1,
52    SourceHigh = 2,
53    SourceLowHigh = 3,
54    TargetLow = 4,
55    TargetHigh = 5,
56    TargetLowHigh = 6,
57    /// The new range is the intersection of the source and target ranges: the greatest of the
58    /// two low levels, and the least of the two high levels.
59    Glblub = 7,
60}
61
62/// Set of rules for computing default security context fields for a class.
63#[derive(Debug, Parse, Serialize, Validate)]
64pub struct ClassDefaults {
65    default_user: ClassDefault,
66    default_role: ClassDefault,
67    default_range: ClassDefaultRange,
68    default_type: ClassDefault,
69}
70
71impl ClassDefaults {
72    pub fn user(&self) -> ClassDefault {
73        self.default_user
74    }
75
76    pub fn role(&self) -> ClassDefault {
77        self.default_role
78    }
79
80    pub fn range(&self) -> ClassDefaultRange {
81        self.default_range
82    }
83
84    pub fn type_(&self) -> ClassDefault {
85        self.default_type
86    }
87}
88
89/// Parsed SELinux object class definition, including permissions and constraints.
90#[derive(Debug, HasName, HasPolicyId)]
91pub struct Class {
92    id: ClassId,
93    name: Box<[u8]>,
94    common_name: Box<[u8]>,
95    /// Included in the policy to allow allocation of index structures to be optimized.
96    permission_primary_names_count: u32,
97    permissions: IdAndNameIndexed<Box<[Permission]>>,
98    constraints: Box<[Constraint]>,
99    validate_transitions: Array<ConstraintTerm>,
100    defaults: ClassDefaults,
101}
102
103impl Class {
104    /// Name of the `common` from which this class inherits.
105    ///
106    /// For example, `common file { common_file_perm }` and
107    /// `class file inherits file { file_perm }` yields a `Class` object
108    /// for `file` with `self.common_name() == b"file"`.
109    pub(super) fn common_name(&self) -> &[u8] {
110        &self.common_name
111    }
112
113    pub(super) fn permissions(&self) -> &IdAndNameIndexed<Box<[Permission]>> {
114        &self.permissions
115    }
116
117    pub(super) fn constraints(&self) -> &[Constraint] {
118        &self.constraints
119    }
120
121    #[cfg(test)]
122    pub(super) fn validate_transitions(&self) -> &[ConstraintTerm] {
123        &self.validate_transitions
124    }
125
126    pub(super) fn defaults(&self) -> &ClassDefaults {
127        &self.defaults
128    }
129}
130
131impl Parse for Class {
132    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
133        let metadata = BinaryClassMetadata::parse(cursor)?;
134
135        let id_val = metadata.id;
136        let id = ClassId::from_u32(id_val).ok_or(ParseError::InvalidId { value: id_val })?;
137
138        let name_len = metadata.key_length as usize;
139        let name = Box::from(cursor.read_bytes(name_len)?);
140
141        let common_name_len = metadata.common_key_length as usize;
142        let common_name = Box::from(cursor.read_bytes(common_name_len)?);
143
144        let permissions_count = metadata.permission_count as usize;
145        let permissions = IdAndNameIndexed::new(cursor.parse_elements(permissions_count)?)?;
146
147        let constraint_count = metadata.constraint_count as usize;
148        let constraints = cursor.parse_elements(constraint_count)?;
149
150        let validate_transitions = Array::<ConstraintTerm>::parse(cursor)?;
151        let defaults = ClassDefaults::parse(cursor)?;
152
153        Ok(Self {
154            id,
155            name,
156            common_name,
157            permission_primary_names_count: metadata.permission_primary_names_count,
158            permissions,
159            constraints,
160            validate_transitions,
161            defaults,
162        })
163    }
164}
165
166impl Serialize for Class {
167    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
168        let metadata = BinaryClassMetadata {
169            key_length: self.name.len() as u32,
170            common_key_length: self.common_name.len() as u32,
171            id: self.id.as_u32(),
172            permission_primary_names_count: self.permission_primary_names_count,
173            permission_count: self.permissions.len() as u32,
174            constraint_count: self.constraints.len() as u32,
175        };
176        metadata.serialize(writer)?;
177
178        writer.write_bytes(&self.name);
179        writer.write_bytes(&self.common_name);
180
181        self.permissions.serialize(writer)?;
182        self.constraints.serialize(writer)?;
183        self.validate_transitions.serialize(writer)?;
184        self.defaults.serialize(writer)?;
185        Ok(())
186    }
187}
188
189impl Validate for Class {
190    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
191        self.permissions.validate(policy)?;
192        self.constraints.validate(policy)?;
193        self.validate_transitions.validate(policy)?;
194        self.defaults.validate(policy)?;
195
196        let mut common_permissions_count = 0;
197        if !self.common_name.is_empty() {
198            let common_symbol =
199                policy.common_symbols().get_by_name(&self.common_name).ok_or_else(|| {
200                    ValidateError::UndefinedCommonSymbol { name: self.common_name.as_ref().into() }
201                })?;
202            common_permissions_count = common_symbol.permissions().len();
203        }
204
205        let expected_at_most = self.permissions.len() + common_permissions_count;
206        if self.permission_primary_names_count > expected_at_most as u32 {
207            return Err(ValidateError::InvalidPrimaryNamesCount {
208                expected_at_most: expected_at_most as u32,
209                found: self.permission_primary_names_count,
210            });
211        }
212
213        Ok(())
214    }
215}
216
217impl Validate for ClassId {
218    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
219        policy
220            .classes()
221            .get_by_id(*self)
222            .map(|_| ())
223            .ok_or_else(|| ValidateError::UnknownId { kind: "class", id: self.as_u32() })
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::{PolicyCursor, *};
230    use crate::policy::metadata::PolicyVersion;
231    use crate::policy::parser::PolicyWriter;
232    use crate::policy::traits::{HasName, HasPolicyId};
233
234    #[test]
235    fn test_class_defaults_parse_and_serialize() {
236        let data = [
237            1, 0, 0, 0, // default_user = 1 (Source)
238            2, 0, 0, 0, // default_role = 2 (Target)
239            6, 0, 0, 0, // default_range = 6 (TargetLowHigh)
240            1, 0, 0, 0, // default_type = 1 (Source)
241        ];
242        let mut cursor = PolicyCursor::new(&data);
243        let defaults = ClassDefaults::parse(&mut cursor).unwrap();
244        assert_eq!(defaults.user(), ClassDefault::Source);
245        assert_eq!(defaults.role(), ClassDefault::Target);
246        assert_eq!(defaults.range(), ClassDefaultRange::TargetLowHigh);
247        assert_eq!(defaults.type_(), ClassDefault::Source);
248
249        let mut writer = Vec::new();
250        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
251        defaults.serialize(&mut policy_writer).unwrap();
252        assert_eq!(writer, data);
253    }
254
255    #[test]
256    fn test_minimal_class_parse_and_serialize() {
257        let data = [
258            // BinaryClassMetadata
259            4, 0, 0, 0, // key_length = 4
260            0, 0, 0, 0, // common_key_length = 0
261            1, 0, 0, 0, // id = 1
262            0, 0, 0, 0, // permission_primary_names_count = 0
263            0, 0, 0, 0, // permission_count = 0
264            0, 0, 0, 0, // constraint_count = 0
265            116, 101, 115, 116, // name: "test"
266            0, 0, 0, 0, // validate_transitions (Array count = 0)
267            // defaults (all Unspecified = 0)
268            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
269        ];
270        let mut cursor = PolicyCursor::new(&data);
271        let class = Class::parse(&mut cursor).unwrap();
272        assert_eq!(class.id(), ClassId::from_u32(1).unwrap());
273        assert_eq!(class.name(), b"test");
274        assert!(class.common_name().is_empty());
275        assert!(class.permissions().is_empty());
276        assert!(class.constraints().is_empty());
277        assert!(class.validate_transitions().is_empty());
278
279        let mut writer = Vec::new();
280        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
281        class.serialize(&mut policy_writer).unwrap();
282        assert_eq!(writer, data);
283    }
284}