1use crate::policy::{Policy, SecurityContext, SecurityContextError};
6use crate::{InitialSid, ReferenceInitialSid, 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![
41 Entry::Invalid { context_string: Vec::new() };
42 ReferenceInitialSid::FirstUnused as usize
43 ],
44 )
45 }
46
47 pub fn new_from_previous(policy: Arc<Policy>, previous: &Self) -> Self {
48 let mut new_entries = vec![
49 Entry::Invalid { context_string: Vec::new() };
50 ReferenceInitialSid::FirstUnused as usize
51 ];
52 new_entries.reserve(previous.entries.len());
53
54 new_entries.extend(
58 previous.entries[ReferenceInitialSid::FirstUnused as usize..].iter().map(
59 |previous_entry| {
60 let serialized_context = match previous_entry {
61 Entry::Valid { security_context } => {
62 previous.policy.serialize_security_context(&security_context)
63 }
64 Entry::Invalid { context_string } => context_string.clone(),
65 };
66 let context =
67 policy.parse_security_context(serialized_context.as_slice().into());
68 if let Ok(context) = context {
69 Entry::Valid { security_context: context }
70 } else {
71 Entry::Invalid { context_string: serialized_context }
72 }
73 },
74 ),
75 );
76
77 Self::new_from(policy, new_entries)
78 }
79
80 pub fn security_context_to_existing_sid(
84 &self,
85 security_context: &SecurityContext,
86 ) -> Option<SecurityId> {
87 let index = &self.entries[ReferenceInitialSid::FirstUnused as usize..]
88 .iter()
89 .position(|entry| match entry {
90 Entry::Valid { security_context: entry_security_context } => {
91 security_context == entry_security_context
92 }
93 Entry::Invalid { .. } => false,
94 })
95 .map(|slice_relative_index| {
96 slice_relative_index + (ReferenceInitialSid::FirstUnused as usize)
97 })?;
98 Some(SecurityId(NonZeroU32::new(*index as u32).unwrap()))
99 }
100
101 pub fn security_context_to_sid(
107 &mut self,
108 security_context: &SecurityContext,
109 ) -> Result<SecurityId, SecurityContextError> {
110 if let Some(sid) = self.security_context_to_existing_sid(security_context) {
111 return Ok(sid);
112 }
113 self.policy.validate_security_context(security_context)?;
114 let index = self.entries.len();
115 self.entries.push(Entry::Valid { security_context: security_context.clone() });
116 Ok(SecurityId(NonZeroU32::new(index as u32).unwrap()))
117 }
118
119 pub fn sid_to_security_context(&self, sid: SecurityId) -> &SecurityContext {
123 &self.try_sid_to_security_context(sid).unwrap_or_else(|| {
124 self.try_sid_to_security_context(InitialSid::Unlabeled.into()).unwrap()
125 })
126 }
127
128 pub fn try_sid_to_security_context(&self, sid: SecurityId) -> Option<&SecurityContext> {
131 match &self.entries[sid.0.get() as usize] {
132 Entry::Valid { security_context } => Some(&security_context),
133 Entry::Invalid { .. } => None,
134 }
135 }
136
137 fn new_from(policy: Arc<Policy>, mut new_entries: Vec<Entry>) -> Self {
138 for initial_sid in InitialSid::all_variants() {
139 let initial_context = policy.initial_context(*initial_sid);
140 new_entries[*initial_sid as usize] =
141 Entry::Valid { security_context: initial_context.clone() };
142 }
143
144 SidTable { policy, entries: new_entries }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 use crate::policy::parse_policy_by_value;
153
154 const TESTS_BINARY_POLICY: &[u8] =
155 include_bytes!("../testdata/micro_policies/security_server_tests_policy.pp");
156
157 fn test_policy() -> Arc<Policy> {
158 let unvalidated = parse_policy_by_value(TESTS_BINARY_POLICY.to_vec()).unwrap();
159 Arc::new(unvalidated.validate().unwrap())
160 }
161
162 #[test]
163 fn sid_to_security_context() {
164 let policy = test_policy();
165 let security_context = policy
166 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
167 .unwrap();
168 let mut sid_table = SidTable::new(policy);
169 let sid = sid_table.security_context_to_sid(&security_context).unwrap();
170 assert_eq!(*sid_table.sid_to_security_context(sid), security_context);
171 }
172
173 #[test]
174 fn sids_for_different_security_contexts_differ() {
175 let policy = test_policy();
176 let mut sid_table = SidTable::new(policy.clone());
177 let sid1 = sid_table.security_context_to_sid(
178 &policy.parse_security_context(b"user0:object_r:type0:s0".into()).unwrap(),
179 );
180 let sid2 = sid_table.security_context_to_sid(
181 &policy
182 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
183 .unwrap(),
184 );
185 assert_ne!(sid1, sid2);
186 }
187
188 #[test]
189 fn sids_for_same_security_context_are_equal() {
190 let policy = test_policy();
191 let security_context = policy
192 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
193 .unwrap();
194 let mut sid_table = SidTable::new(policy);
195 let sid_count_before = sid_table.entries.len();
196 let sid1 = sid_table.security_context_to_sid(&security_context);
197 let sid2 = sid_table.security_context_to_sid(&security_context);
198 assert_eq!(sid1, sid2);
199 assert_eq!(sid_table.entries.len(), sid_count_before + 1);
200 }
201
202 #[test]
203 fn sids_allocated_outside_initial_range() {
204 let policy = test_policy();
205 let security_context = policy
206 .parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
207 .unwrap();
208 let mut sid_table = SidTable::new(policy);
209 let sid_count_before = sid_table.entries.len();
210 let sid = sid_table.security_context_to_sid(&security_context).unwrap();
211 assert_eq!(sid_table.entries.len(), sid_count_before + 1);
212 assert!(sid.0.get() >= ReferenceInitialSid::FirstUnused as u32);
213 }
214
215 #[test]
216 fn initial_sids_remapped_to_dynamic_sids() {
217 let file_initial_sid = InitialSid::File.into();
218 let policy = test_policy();
219 let mut sid_table = SidTable::new(policy);
220 let file_initial_security_context = sid_table.sid_to_security_context(file_initial_sid);
221 let file_dynamic_sid =
222 sid_table.security_context_to_sid(&file_initial_security_context.clone()).unwrap();
223 assert_ne!(file_initial_sid.0.get(), file_dynamic_sid.0.get());
224 assert!(file_dynamic_sid.0.get() >= ReferenceInitialSid::FirstUnused as u32);
225 }
226}