Skip to main content

selinux/new_policy/
roles.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 super::bitmap::IdSet;
6use super::error::{ParseError, SerializeError, ValidateError};
7use super::id_type::IdType;
8use super::parser::PolicyCursor;
9use super::traits::{Parse, PolicyId, Serialize, Validate};
10use super::{NewPolicy, TypeSet};
11
12use selinux_policy_derive::{HasName, HasPolicyId, Parse, Serialize, Validate};
13
14/// Tag type for type safety of policy role identifiers.
15#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
16pub struct RoleTag;
17
18/// Identifies a role within a policy.
19pub type RoleId = IdType<std::num::NonZeroU16, RoleTag>;
20
21/// Set of [`RoleId`]s.
22pub type RoleSet = IdSet<RoleId>;
23
24#[derive(Parse, Serialize)]
25struct BinaryRoleMetadata {
26    key_length: u32,
27    id: u32,
28    bounds: u32,
29}
30
31/// Parsed SELinux [`Role`] definition.
32#[derive(Debug, Clone, PartialEq, Eq, Validate, HasName, HasPolicyId)]
33pub struct Role {
34    id: RoleId,
35    name: Box<[u8]>,
36    bounds: Option<RoleId>,
37    dominates: RoleSet,
38    types: TypeSet,
39}
40
41impl Role {
42    pub fn bounds(&self) -> Option<RoleId> {
43        self.bounds
44    }
45
46    pub fn dominates(&self) -> &RoleSet {
47        &self.dominates
48    }
49
50    pub fn types(&self) -> &TypeSet {
51        &self.types
52    }
53}
54
55impl Parse for Role {
56    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
57        let metadata = BinaryRoleMetadata::parse(cursor)?;
58        let name = Box::from(cursor.read_bytes(metadata.key_length as usize)?);
59        let dominates = RoleSet::parse(cursor)?;
60        let types = TypeSet::parse(cursor)?;
61
62        let bounds = RoleId::from_u32(metadata.bounds);
63        let id =
64            RoleId::from_u32(metadata.id).ok_or(ParseError::InvalidId { value: metadata.id })?;
65
66        Ok(Self { id, name, bounds, dominates, types })
67    }
68}
69
70impl Serialize for Role {
71    fn serialize(&self, writer: &mut Vec<u8>) -> Result<(), SerializeError> {
72        let metadata = BinaryRoleMetadata {
73            key_length: self.name.len() as u32,
74            id: self.id.as_u32(),
75            bounds: self.bounds.map_or(0, |id| id.as_u32()),
76        };
77        metadata.serialize(writer)?;
78        writer.extend_from_slice(&self.name);
79        self.dominates.serialize(writer)?;
80        self.types.serialize(writer)?;
81        Ok(())
82    }
83}
84impl Validate for RoleId {
85    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
86        policy
87            .roles()
88            .get_by_id(*self)
89            .map(|_| ())
90            .ok_or_else(|| ValidateError::UnknownId { kind: "role", id: self.as_u32() })
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use crate::new_policy::traits::{HasName, HasPolicyId};
98
99    #[test]
100    fn test_role_parse_and_serialize() {
101        let data = [
102            // BinaryRoleMetadata
103            4, 0, 0, 0, // key_length = 4
104            1, 0, 0, 0, // id = 1
105            0, 0, 0, 0, // bounds = 0
106            // name: "test"
107            b't', b'e', b's', b't', // dominates (empty ExtensibleBitmap)
108            64, 0, 0, 0, // map_item_size_bits = 64
109            0, 0, 0, 0, // high_bit = 0
110            0, 0, 0, 0, // items_count = 0
111            // types (empty ExtensibleBitmap)
112            64, 0, 0, 0, // map_item_size_bits = 64
113            0, 0, 0, 0, // high_bit = 0
114            0, 0, 0, 0, // items_count = 0
115        ];
116        let mut cursor = PolicyCursor::new(&data);
117        let role = Role::parse(&mut cursor).unwrap();
118        assert_eq!(role.id(), RoleId::from_u32(1).unwrap());
119        assert_eq!(role.name(), b"test");
120        assert!(role.bounds().is_none());
121        // dominates and types are empty dynamically validated by round-trip
122
123        let mut writer = Vec::new();
124        role.serialize(&mut writer).unwrap();
125        assert_eq!(writer, data);
126    }
127}