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 mut permissions = Vec::with_capacity(permissions_count);
146        for _ in 0..permissions_count {
147            permissions.push(Permission::parse(cursor)?);
148        }
149        let permissions = IdAndNameIndexed::new(permissions.into_boxed_slice())?;
150
151        let constraint_count = metadata.constraint_count as usize;
152        let mut constraints = Vec::with_capacity(constraint_count);
153        for _ in 0..constraint_count {
154            constraints.push(Constraint::parse(cursor)?);
155        }
156        let constraints = constraints.into_boxed_slice();
157
158        let validate_transitions = Array::<ConstraintTerm>::parse(cursor)?;
159        let defaults = ClassDefaults::parse(cursor)?;
160
161        Ok(Self {
162            id,
163            name,
164            common_name,
165            permission_primary_names_count: metadata.permission_primary_names_count,
166            permissions,
167            constraints,
168            validate_transitions,
169            defaults,
170        })
171    }
172}
173
174impl Serialize for Class {
175    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
176        let metadata = BinaryClassMetadata {
177            key_length: self.name.len() as u32,
178            common_key_length: self.common_name.len() as u32,
179            id: self.id.as_u32(),
180            permission_primary_names_count: self.permission_primary_names_count,
181            permission_count: self.permissions.len() as u32,
182            constraint_count: self.constraints.len() as u32,
183        };
184        metadata.serialize(writer)?;
185
186        writer.write_bytes(&self.name);
187        writer.write_bytes(&self.common_name);
188
189        self.permissions.serialize(writer)?;
190        self.constraints.serialize(writer)?;
191        self.validate_transitions.serialize(writer)?;
192        self.defaults.serialize(writer)?;
193        Ok(())
194    }
195}
196
197impl Validate for Class {
198    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
199        self.permissions.validate(policy)?;
200        self.constraints.validate(policy)?;
201        self.validate_transitions.validate(policy)?;
202        self.defaults.validate(policy)?;
203
204        let mut common_permissions_count = 0;
205        if !self.common_name.is_empty() {
206            let common_symbol =
207                policy.common_symbols().get_by_name(&self.common_name).ok_or_else(|| {
208                    ValidateError::UndefinedCommonSymbol { name: self.common_name.as_ref().into() }
209                })?;
210            common_permissions_count = common_symbol.permissions().len();
211        }
212
213        let expected_at_most = self.permissions.len() + common_permissions_count;
214        if self.permission_primary_names_count > expected_at_most as u32 {
215            return Err(ValidateError::InvalidPrimaryNamesCount {
216                expected_at_most: expected_at_most as u32,
217                found: self.permission_primary_names_count,
218            });
219        }
220
221        Ok(())
222    }
223}
224
225impl Validate for ClassId {
226    fn validate(&self, policy: &Policy) -> Result<(), ValidateError> {
227        policy
228            .classes()
229            .get_by_id(*self)
230            .map(|_| ())
231            .ok_or_else(|| ValidateError::UnknownId { kind: "class", id: self.as_u32() })
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::{PolicyCursor, *};
238    use crate::policy::metadata::PolicyVersion;
239    use crate::policy::parser::PolicyWriter;
240    use crate::policy::traits::{HasName, HasPolicyId};
241
242    #[test]
243    fn test_class_defaults_parse_and_serialize() {
244        let data = [
245            1, 0, 0, 0, // default_user = 1 (Source)
246            2, 0, 0, 0, // default_role = 2 (Target)
247            6, 0, 0, 0, // default_range = 6 (TargetLowHigh)
248            1, 0, 0, 0, // default_type = 1 (Source)
249        ];
250        let mut cursor = PolicyCursor::new(&data);
251        let defaults = ClassDefaults::parse(&mut cursor).unwrap();
252        assert_eq!(defaults.user(), ClassDefault::Source);
253        assert_eq!(defaults.role(), ClassDefault::Target);
254        assert_eq!(defaults.range(), ClassDefaultRange::TargetLowHigh);
255        assert_eq!(defaults.type_(), ClassDefault::Source);
256
257        let mut writer = Vec::new();
258        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
259        defaults.serialize(&mut policy_writer).unwrap();
260        assert_eq!(writer, data);
261    }
262
263    #[test]
264    fn test_minimal_class_parse_and_serialize() {
265        let data = [
266            // BinaryClassMetadata
267            4, 0, 0, 0, // key_length = 4
268            0, 0, 0, 0, // common_key_length = 0
269            1, 0, 0, 0, // id = 1
270            0, 0, 0, 0, // permission_primary_names_count = 0
271            0, 0, 0, 0, // permission_count = 0
272            0, 0, 0, 0, // constraint_count = 0
273            116, 101, 115, 116, // name: "test"
274            0, 0, 0, 0, // validate_transitions (Array count = 0)
275            // defaults (all Unspecified = 0)
276            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
277        ];
278        let mut cursor = PolicyCursor::new(&data);
279        let class = Class::parse(&mut cursor).unwrap();
280        assert_eq!(class.id(), ClassId::from_u32(1).unwrap());
281        assert_eq!(class.name(), b"test");
282        assert!(class.common_name().is_empty());
283        assert!(class.permissions().is_empty());
284        assert!(class.constraints().is_empty());
285        assert!(class.validate_transitions().is_empty());
286
287        let mut writer = Vec::new();
288        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut writer);
289        class.serialize(&mut policy_writer).unwrap();
290        assert_eq!(writer, data);
291    }
292}