use crate::policy::parser::ByValue;
use crate::policy::{Policy, SecurityContext, SecurityContextError};
use crate::{InitialSid, SecurityId, FIRST_UNUSED_SID};
use std::num::NonZeroU32;
use std::sync::Arc;
#[derive(Clone)]
enum Entry {
Valid { security_context: SecurityContext },
Invalid { context_string: Vec<u8> },
}
pub struct SidTable {
policy: Arc<Policy<ByValue<Vec<u8>>>>,
entries: Vec<Entry>,
}
impl SidTable {
pub fn new(policy: Arc<Policy<ByValue<Vec<u8>>>>) -> Self {
Self::new_from(
policy,
vec![Entry::Invalid { context_string: Vec::new() }; FIRST_UNUSED_SID as usize],
)
}
pub fn new_from_previous(policy: Arc<Policy<ByValue<Vec<u8>>>>, previous: &Self) -> Self {
let mut new_entries =
vec![Entry::Invalid { context_string: Vec::new() }; FIRST_UNUSED_SID as usize];
new_entries.reserve(previous.entries.len());
new_entries.extend(previous.entries[FIRST_UNUSED_SID as usize..].iter().map(
|previous_entry| {
let serialized_context = match previous_entry {
Entry::Valid { security_context } => {
previous.policy.serialize_security_context(&security_context)
}
Entry::Invalid { context_string } => context_string.clone(),
};
let context = policy.parse_security_context(serialized_context.as_slice().into());
if let Ok(context) = context {
Entry::Valid { security_context: context }
} else {
Entry::Invalid { context_string: serialized_context }
}
},
));
Self::new_from(policy, new_entries)
}
pub fn security_context_to_sid(
&mut self,
security_context: &SecurityContext,
) -> Result<SecurityId, SecurityContextError> {
let existing = &self.entries[FIRST_UNUSED_SID as usize..]
.iter()
.position(|entry| match entry {
Entry::Valid { security_context: entry_security_context } => {
security_context == entry_security_context
}
Entry::Invalid { .. } => false,
})
.map(|slice_relative_index| slice_relative_index + (FIRST_UNUSED_SID as usize));
let index = if let Some(index) = existing {
*index
} else {
self.policy.validate_security_context(security_context)?;
let index = self.entries.len();
self.entries.push(Entry::Valid { security_context: security_context.clone() });
index
};
Ok(SecurityId(NonZeroU32::new(index as u32).unwrap()))
}
pub fn sid_to_security_context(&self, sid: SecurityId) -> &SecurityContext {
&self.try_sid_to_security_context(sid).unwrap_or_else(|| {
self.try_sid_to_security_context(SecurityId::initial(InitialSid::Unlabeled)).unwrap()
})
}
pub fn try_sid_to_security_context(&self, sid: SecurityId) -> Option<&SecurityContext> {
match &self.entries[sid.0.get() as usize] {
Entry::Valid { security_context } => Some(&security_context),
Entry::Invalid { .. } => None,
}
}
fn new_from(policy: Arc<Policy<ByValue<Vec<u8>>>>, mut new_entries: Vec<Entry>) -> Self {
for initial_sid in InitialSid::all_variants() {
let initial_context = policy.initial_context(initial_sid);
new_entries[initial_sid as usize] =
Entry::Valid { security_context: initial_context.clone() };
}
SidTable { policy, entries: new_entries }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::policy::parse_policy_by_value;
const TESTS_BINARY_POLICY: &[u8] =
include_bytes!("../testdata/micro_policies/security_server_tests_policy.pp");
fn test_policy() -> Arc<Policy<ByValue<Vec<u8>>>> {
let (unvalidated, _binary) = parse_policy_by_value(TESTS_BINARY_POLICY.to_vec()).unwrap();
Arc::new(unvalidated.validate().unwrap())
}
#[test]
fn sid_to_security_context() {
let policy = test_policy();
let security_context = policy
.parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
.unwrap();
let mut sid_table = SidTable::new(policy);
let sid = sid_table.security_context_to_sid(&security_context).unwrap();
assert_eq!(*sid_table.sid_to_security_context(sid), security_context);
}
#[test]
fn sids_for_different_security_contexts_differ() {
let policy = test_policy();
let mut sid_table = SidTable::new(policy.clone());
let sid1 = sid_table.security_context_to_sid(
&policy.parse_security_context(b"user0:object_r:type0:s0".into()).unwrap(),
);
let sid2 = sid_table.security_context_to_sid(
&policy
.parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
.unwrap(),
);
assert_ne!(sid1, sid2);
}
#[test]
fn sids_for_same_security_context_are_equal() {
let policy = test_policy();
let security_context = policy
.parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
.unwrap();
let mut sid_table = SidTable::new(policy);
let sid_count_before = sid_table.entries.len();
let sid1 = sid_table.security_context_to_sid(&security_context);
let sid2 = sid_table.security_context_to_sid(&security_context);
assert_eq!(sid1, sid2);
assert_eq!(sid_table.entries.len(), sid_count_before + 1);
}
#[test]
fn sids_allocated_outside_initial_range() {
let policy = test_policy();
let security_context = policy
.parse_security_context(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
.unwrap();
let mut sid_table = SidTable::new(policy);
let sid_count_before = sid_table.entries.len();
let sid = sid_table.security_context_to_sid(&security_context).unwrap();
assert_eq!(sid_table.entries.len(), sid_count_before + 1);
assert!(sid.0.get() >= FIRST_UNUSED_SID);
}
#[test]
fn initial_sids_remapped_to_dynamic_sids() {
let file_initial_sid = SecurityId::initial(InitialSid::File);
let policy = test_policy();
let mut sid_table = SidTable::new(policy);
let file_initial_security_context = sid_table.sid_to_security_context(file_initial_sid);
let file_dynamic_sid =
sid_table.security_context_to_sid(&file_initial_security_context.clone()).unwrap();
assert_ne!(file_initial_sid.0.get(), file_dynamic_sid.0.get());
assert!(file_dynamic_sid.0.get() >= FIRST_UNUSED_SID);
}
}