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