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