Skip to main content

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