1use crate::policy::{Policy, SecurityContext, SecurityContextError};
6use crate::{FIRST_UNUSED_SID, InitialSid, SecurityId};
7
8use std::num::NonZeroU32;
9use std::sync::Arc;
10
11#[derive(Clone)]
12enum Entry {
13 Valid { security_context: SecurityContext },
15
16 Invalid { context_string: Vec<u8> },
20}
21
22pub struct SidTable {
27 policy: Arc<Policy>,
29
30 entries: Vec<Entry>,
34}
35
36impl SidTable {
37 pub fn new(policy: Arc<Policy>) -> Self {
38 Self::new_from(
39 policy,
40 vec![Entry::Invalid { context_string: Vec::new() }; FIRST_UNUSED_SID as usize],
41 )
42 }
43
44 pub fn new_from_previous(policy: Arc<Policy>, previous: &Self) -> Self {
45 let mut new_entries =
46 vec![Entry::Invalid { context_string: Vec::new() }; FIRST_UNUSED_SID as usize];
47 new_entries.reserve(previous.entries.len());
48
49 new_entries.extend(previous.entries[FIRST_UNUSED_SID as usize..].iter().map(
53 |previous_entry| {
54 let serialized_context = match previous_entry {
55 Entry::Valid { security_context } => {
56 previous.policy.serialize_security_context(&security_context)
57 }
58 Entry::Invalid { context_string } => context_string.clone(),
59 };
60 let context = policy.parse_security_context(serialized_context.as_slice().into());
61 if let Ok(context) = context {
62 Entry::Valid { security_context: context }
63 } else {
64 Entry::Invalid { context_string: serialized_context }
65 }
66 },
67 ));
68
69 Self::new_from(policy, new_entries)
70 }
71
72 pub fn security_context_to_sid(
78 &mut self,
79 security_context: &SecurityContext,
80 ) -> Result<SecurityId, SecurityContextError> {
81 let existing = &self.entries[FIRST_UNUSED_SID as usize..]
82 .iter()
83 .position(|entry| match entry {
84 Entry::Valid { security_context: entry_security_context } => {
85 security_context == entry_security_context
86 }
87 Entry::Invalid { .. } => false,
88 })
89 .map(|slice_relative_index| slice_relative_index + (FIRST_UNUSED_SID as usize));
90 let index = if let Some(index) = existing {
91 *index
92 } else {
93 self.policy.validate_security_context(security_context)?;
94 let index = self.entries.len();
95 self.entries.push(Entry::Valid { security_context: security_context.clone() });
96 index
97 };
98 Ok(SecurityId(NonZeroU32::new(index as u32).unwrap()))
99 }
100
101 pub fn sid_to_security_context(&self, sid: SecurityId) -> &SecurityContext {
105 &self.try_sid_to_security_context(sid).unwrap_or_else(|| {
106 self.try_sid_to_security_context(InitialSid::Unlabeled.into()).unwrap()
107 })
108 }
109
110 pub fn try_sid_to_security_context(&self, sid: SecurityId) -> Option<&SecurityContext> {
113 match &self.entries[sid.0.get() as usize] {
114 Entry::Valid { security_context } => Some(&security_context),
115 Entry::Invalid { .. } => None,
116 }
117 }
118
119 fn new_from(policy: Arc<Policy>, mut new_entries: Vec<Entry>) -> Self {
120 for initial_sid in InitialSid::all_variants() {
121 let initial_context = policy.initial_context(*initial_sid);
122 new_entries[*initial_sid as usize] =
123 Entry::Valid { security_context: initial_context.clone() };
124 }
125
126 SidTable { policy, entries: new_entries }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 use crate::policy::parse_policy_by_value;
135
136 const TESTS_BINARY_POLICY: &[u8] =
137 include_bytes!("../testdata/micro_policies/security_server_tests_policy.pp");
138
139 fn test_policy() -> Arc<Policy> {
140 let unvalidated = parse_policy_by_value(TESTS_BINARY_POLICY.to_vec()).unwrap();
141 Arc::new(unvalidated.validate().unwrap())
142 }
143
144 #[test]
145 fn sid_to_security_context() {
146 let policy = test_policy();
147 let security_context = policy
148 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
149 .unwrap();
150 let mut sid_table = SidTable::new(policy);
151 let sid = sid_table.security_context_to_sid(&security_context).unwrap();
152 assert_eq!(*sid_table.sid_to_security_context(sid), security_context);
153 }
154
155 #[test]
156 fn sids_for_different_security_contexts_differ() {
157 let policy = test_policy();
158 let mut sid_table = SidTable::new(policy.clone());
159 let sid1 = sid_table.security_context_to_sid(
160 &policy.parse_security_context(b"user0:object_r:type0:s0".into()).unwrap(),
161 );
162 let sid2 = sid_table.security_context_to_sid(
163 &policy
164 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
165 .unwrap(),
166 );
167 assert_ne!(sid1, sid2);
168 }
169
170 #[test]
171 fn sids_for_same_security_context_are_equal() {
172 let policy = test_policy();
173 let security_context = policy
174 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
175 .unwrap();
176 let mut sid_table = SidTable::new(policy);
177 let sid_count_before = sid_table.entries.len();
178 let sid1 = sid_table.security_context_to_sid(&security_context);
179 let sid2 = sid_table.security_context_to_sid(&security_context);
180 assert_eq!(sid1, sid2);
181 assert_eq!(sid_table.entries.len(), sid_count_before + 1);
182 }
183
184 #[test]
185 fn sids_allocated_outside_initial_range() {
186 let policy = test_policy();
187 let security_context = policy
188 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
189 .unwrap();
190 let mut sid_table = SidTable::new(policy);
191 let sid_count_before = sid_table.entries.len();
192 let sid = sid_table.security_context_to_sid(&security_context).unwrap();
193 assert_eq!(sid_table.entries.len(), sid_count_before + 1);
194 assert!(sid.0.get() >= FIRST_UNUSED_SID);
195 }
196
197 #[test]
198 fn initial_sids_remapped_to_dynamic_sids() {
199 let file_initial_sid = InitialSid::File.into();
200 let policy = test_policy();
201 let mut sid_table = SidTable::new(policy);
202 let file_initial_security_context = sid_table.sid_to_security_context(file_initial_sid);
203 let file_dynamic_sid =
204 sid_table.security_context_to_sid(&file_initial_security_context.clone()).unwrap();
205 assert_ne!(file_initial_sid.0.get(), file_dynamic_sid.0.get());
206 assert!(file_dynamic_sid.0.get() >= FIRST_UNUSED_SID);
207 }
208}