pub mod error;
pub mod index;
pub mod metadata;
pub mod parsed_policy;
pub mod parser;
mod arrays;
mod extensible_bitmap;
mod security_context;
mod symbols;
pub use security_context::{SecurityContext, SecurityContextError};
use {
anyhow::Context as _,
error::{NewSecurityContextError, ParseError, QueryError},
index::PolicyIndex,
metadata::HandleUnknown,
parsed_policy::ParsedPolicy,
parser::ByValue,
parser::{ByRef, ParseStrategy},
selinux_common::{self as sc, ClassPermission as _, FileClass, ObjectClass},
std::{fmt::Debug, marker::PhantomData, num::NonZeroU32, ops::Deref},
zerocopy::{little_endian as le, ByteSlice, FromBytes, NoCell, Ref, Unaligned},
};
pub const SUPPORTED_POLICY_VERSION: u32 = 33;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct UserId(NonZeroU32);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct RoleId(NonZeroU32);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct TypeId(NonZeroU32);
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
pub struct SensitivityId(NonZeroU32);
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
pub struct CategoryId(NonZeroU32);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AccessVector(u32);
impl AccessVector {
pub const NONE: AccessVector = AccessVector(0);
pub const ALL: AccessVector = AccessVector(std::u32::MAX);
pub(crate) fn from_raw(access_vector: u32) -> Self {
Self(access_vector)
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn into_raw(self) -> u32 {
self.0
}
}
impl std::ops::BitAnd for AccessVector {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
AccessVector(self.0 & rhs.0)
}
}
impl std::ops::BitOr for AccessVector {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
AccessVector(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for AccessVector {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
}
}
pub fn parse_policy_by_value(
binary_policy: Vec<u8>,
) -> Result<(Unvalidated<ByValue<Vec<u8>>>, Vec<u8>), anyhow::Error> {
let (parsed_policy, binary_policy) =
ParsedPolicy::parse(ByValue::new(binary_policy)).context("parsing policy")?;
Ok((Unvalidated(parsed_policy), binary_policy))
}
pub fn parse_policy_by_reference<'a>(
binary_policy: &'a [u8],
) -> Result<Unvalidated<ByRef<&'a [u8]>>, anyhow::Error> {
let (parsed_policy, _) =
ParsedPolicy::parse(ByRef::new(binary_policy)).context("parsing policy")?;
Ok(Unvalidated(parsed_policy))
}
#[derive(Debug)]
pub struct Policy<PS: ParseStrategy>(PolicyIndex<PS>);
impl<PS: ParseStrategy> Policy<PS> {
pub fn policy_version(&self) -> u32 {
self.0.parsed_policy().policy_version()
}
pub fn handle_unknown(&self) -> &HandleUnknown {
self.0.parsed_policy().handle_unknown()
}
pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
self.0
.parsed_policy()
.conditional_booleans()
.iter()
.map(|boolean| (PS::deref_slice(&boolean.data), PS::deref(&boolean.metadata).active()))
.collect()
}
pub fn initial_context(&self, id: sc::InitialSid) -> security_context::SecurityContext {
self.0.initial_context(id).unwrap()
}
pub fn parse_security_context(
&self,
security_context: &[u8],
) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
security_context::SecurityContext::parse(&self.0, security_context)
}
pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
security_context.serialize(&self.0)
}
pub fn new_file_security_context(
&self,
source: &SecurityContext,
target: &SecurityContext,
class: &FileClass,
) -> Result<SecurityContext, NewSecurityContextError> {
self.0.new_file_security_context(source, target, class)
}
pub fn new_security_context(
&self,
source: &SecurityContext,
target: &SecurityContext,
class: &ObjectClass,
) -> Result<SecurityContext, NewSecurityContextError> {
self.0.new_security_context(
source,
target,
class,
source.role(),
source.type_(),
source.low_level(),
source.high_level(),
)
}
pub fn is_explicitly_allowed(
&self,
source_type: TypeId,
target_type: TypeId,
permission: sc::Permission,
) -> Result<bool, QueryError> {
let object_class = permission.class();
let target_class = self.0.class(&object_class);
let permission = self.0.permission(&permission);
self.0.parsed_policy().class_permission_is_explicitly_allowed(
source_type,
target_type,
target_class,
permission,
)
}
pub fn is_explicitly_allowed_custom(
&self,
source_type: TypeId,
target_type: TypeId,
target_class_name: &str,
permission_name: &str,
) -> Result<bool, QueryError> {
self.0.parsed_policy().is_explicitly_allowed_custom(
source_type,
target_type,
target_class_name,
permission_name,
)
}
pub fn compute_explicitly_allowed(
&self,
source_type: TypeId,
target_type: TypeId,
object_class: sc::ObjectClass,
) -> Result<AccessVector, QueryError> {
let target_class = self.0.class(&object_class);
self.0.parsed_policy().compute_explicitly_allowed(source_type, target_type, target_class)
}
pub fn compute_explicitly_allowed_custom(
&self,
source_type: TypeId,
target_type: TypeId,
target_class_name: &str,
) -> Result<AccessVector, QueryError> {
self.0.parsed_policy().compute_explicitly_allowed_custom(
source_type,
target_type,
target_class_name,
)
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn print_permissions(&self) {
let parsed_policy = self.0.parsed_policy();
for class in parsed_policy.classes().into_iter() {
println!("{}", std::str::from_utf8(class.name_bytes()).expect("class name"));
for permission in class.permissions().into_iter() {
println!(
" {}",
std::str::from_utf8(permission.name_bytes()).expect("permission name")
);
}
}
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn type_id_by_name(&self, name: &str) -> TypeId {
self.0.parsed_policy().type_id_by_name(name)
}
}
impl<PS: ParseStrategy> AccessVectorComputer for Policy<PS> {
fn access_vector_from_permission<P: sc::ClassPermission + Into<sc::Permission> + 'static>(
&self,
permission: P,
) -> AccessVector {
let permission = self.0.permission(&permission.into());
AccessVector(1 << (permission.id() - 1))
}
fn access_vector_from_permissions<
'a,
P: sc::ClassPermission + Into<sc::Permission> + 'static,
PI: IntoIterator<Item = P>,
>(
&self,
permissions: PI,
) -> AccessVector {
let mut access_vector = AccessVector::NONE;
for permission in permissions.into_iter() {
access_vector |= self.access_vector_from_permission(permission);
}
access_vector
}
}
impl<PS: ParseStrategy> Validate for Policy<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
self.0.parsed_policy().validate()
}
}
pub struct Unvalidated<PS: ParseStrategy>(ParsedPolicy<PS>);
impl<PS: ParseStrategy> Unvalidated<PS> {
pub fn validate(self) -> Result<Policy<PS>, anyhow::Error> {
Validate::validate(&self.0).context("validating parsed policy")?;
let index = PolicyIndex::new(self.0).context("building index")?;
Ok(Policy(index))
}
#[cfg(feature = "selinux_policy_test_api")]
pub fn parsed_policy(&self) -> &ParsedPolicy<PS> {
&self.0
}
}
pub trait AccessVectorComputer {
fn access_vector_from_permission<P: sc::ClassPermission + Into<sc::Permission> + 'static>(
&self,
permission: P,
) -> AccessVector;
fn access_vector_from_permissions<
'a,
P: sc::ClassPermission + Into<sc::Permission> + 'static,
PI: IntoIterator<Item = P>,
>(
&self,
permissions: PI,
) -> AccessVector;
}
pub trait Parse<PS: ParseStrategy>: Sized {
type Error: Into<anyhow::Error>;
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error>;
}
pub(crate) trait ParseSlice<PS: ParseStrategy>: Sized {
type Error: Into<anyhow::Error>;
fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error>;
}
pub(crate) trait Validate {
type Error: Into<anyhow::Error>;
fn validate(&self) -> Result<(), Self::Error>;
}
pub(crate) trait ValidateArray<M, D> {
type Error: Into<anyhow::Error>;
fn validate_array<'a>(metadata: &'a M, data: &'a [D]) -> Result<(), Self::Error>;
}
pub(crate) trait Counted {
fn count(&self) -> u32;
}
impl<T: Validate> Validate for Option<T> {
type Error = <T as Validate>::Error;
fn validate(&self) -> Result<(), Self::Error> {
match self {
Some(value) => value.validate(),
None => Ok(()),
}
}
}
impl Validate for le::U32 {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Validate for u8 {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Validate for [u8] {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<B: ByteSlice, T: Validate + FromBytes + NoCell> Validate for Ref<B, T> {
type Error = <T as Validate>::Error;
fn validate(&self) -> Result<(), Self::Error> {
self.deref().validate()
}
}
impl<B: ByteSlice, T: Counted + FromBytes + NoCell> Counted for Ref<B, T> {
fn count(&self) -> u32 {
self.deref().count()
}
}
#[derive(Clone, Debug, PartialEq)]
struct Array<PS, M, D> {
metadata: M,
data: D,
_marker: PhantomData<PS>,
}
impl<PS: ParseStrategy, M: Counted + Parse<PS>, D: ParseSlice<PS>> Parse<PS> for Array<PS, M, D> {
type Error = anyhow::Error;
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
let tail = bytes;
let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
let (data, tail) =
D::parse_slice(tail, metadata.count() as usize).map_err(Into::<anyhow::Error>::into)?;
let array = Self { metadata, data, _marker: PhantomData };
Ok((array, tail))
}
}
impl<
T: Clone + Debug + FromBytes + NoCell + PartialEq + Unaligned,
PS: ParseStrategy<Output<T> = T>,
> Parse<PS> for T
{
type Error = anyhow::Error;
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
let num_bytes = bytes.len();
let (data, tail) = PS::parse::<T>(bytes).ok_or(ParseError::MissingData {
type_name: std::any::type_name::<T>(),
type_size: std::mem::size_of::<T>(),
num_bytes,
})?;
Ok((data, tail))
}
}
macro_rules! array_type {
($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty, $metadata_type_name:expr, $data_type_name:expr) => {
#[doc = "An [`Array`] with [`"]
#[doc = $metadata_type_name]
#[doc = "`] metadata and [`"]
#[doc = $data_type_name]
#[doc = "`] data items."]
#[derive(Debug, PartialEq)]
pub(crate) struct $type_name<$parse_strategy: crate::parser::ParseStrategy>(
crate::Array<PS, $metadata_type, $data_type>,
);
impl<PS: crate::parser::ParseStrategy> std::ops::Deref for $type_name<PS> {
type Target = crate::Array<PS, $metadata_type, $data_type>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<PS: crate::parser::ParseStrategy> crate::Parse<PS> for $type_name<PS>
where
Array<PS, $metadata_type, $data_type>: crate::Parse<PS>,
{
type Error = <Array<PS, $metadata_type, $data_type> as crate::Parse<PS>>::Error;
fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
let (array, tail) = Array::<PS, $metadata_type, $data_type>::parse(bytes)?;
Ok((Self(array), tail))
}
}
};
($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty) => {
array_type!(
$type_name,
$parse_strategy,
$metadata_type,
$data_type,
stringify!($metadata_type),
stringify!($data_type)
);
};
}
pub(crate) use array_type;
macro_rules! array_type_validate_deref_both {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = PS::deref(&self.metadata);
metadata.validate()?;
let data = PS::deref_slice(&self.data);
data.validate()?;
Self::validate_array(metadata, data).map_err(Into::<anyhow::Error>::into)
}
}
};
}
pub(crate) use array_type_validate_deref_both;
macro_rules! array_type_validate_deref_data {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = &self.metadata;
metadata.validate()?;
let data = PS::deref_slice(&self.data);
data.validate()?;
Self::validate_array(metadata, data)
}
}
};
}
pub(crate) use array_type_validate_deref_data;
macro_rules! array_type_validate_deref_metadata_data_vec {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = PS::deref(&self.metadata);
metadata.validate()?;
let data = &self.data;
data.validate()?;
Self::validate_array(metadata, data.as_slice())
}
}
};
}
pub(crate) use array_type_validate_deref_metadata_data_vec;
macro_rules! array_type_validate_deref_none_data_vec {
($type_name:ident) => {
impl<PS: crate::parser::ParseStrategy> Validate for $type_name<PS> {
type Error = anyhow::Error;
fn validate(&self) -> Result<(), Self::Error> {
let metadata = &self.metadata;
metadata.validate()?;
let data = &self.data;
data.validate()?;
Self::validate_array(metadata, data.as_slice())
}
}
};
}
pub(crate) use array_type_validate_deref_none_data_vec;
impl<
B: Debug + ByteSlice + PartialEq,
T: Clone + Debug + FromBytes + NoCell + PartialEq + Unaligned,
> Parse<ByRef<B>> for Ref<B, T>
{
type Error = anyhow::Error;
fn parse(bytes: ByRef<B>) -> Result<(Self, ByRef<B>), Self::Error> {
let num_bytes = bytes.len();
let (data, tail) = ByRef::<B>::parse::<T>(bytes).ok_or(ParseError::MissingData {
type_name: std::any::type_name::<T>(),
type_size: std::mem::size_of::<T>(),
num_bytes,
})?;
Ok((data, tail))
}
}
impl<
B: Debug + ByteSlice + PartialEq,
T: Clone + Debug + FromBytes + NoCell + PartialEq + Unaligned,
> ParseSlice<ByRef<B>> for Ref<B, [T]>
{
type Error = anyhow::Error;
fn parse_slice(bytes: ByRef<B>, count: usize) -> Result<(Self, ByRef<B>), Self::Error> {
let num_bytes = bytes.len();
let (data, tail) =
ByRef::<B>::parse_slice::<T>(bytes, count).ok_or(ParseError::MissingSliceData {
type_name: std::any::type_name::<T>(),
type_size: std::mem::size_of::<T>(),
num_items: count,
num_bytes,
})?;
Ok((data, tail))
}
}
impl<PS: ParseStrategy, T: Parse<PS>> ParseSlice<PS> for Vec<T> {
type Error = anyhow::Error;
fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error> {
let mut slice = Vec::with_capacity(count);
let mut tail = bytes;
for _ in 0..count {
let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
slice.push(item);
tail = next_tail;
}
Ok((slice, tail))
}
}
pub mod testing {
use super::AccessVector;
pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
pub const ACCESS_VECTOR_0100: AccessVector = AccessVector(0b0100u32);
pub const ACCESS_VECTOR_1000: AccessVector = AccessVector(0b1000u32);
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use super::error::ValidateError;
pub fn as_parse_error(error: anyhow::Error) -> ParseError {
error.downcast::<ParseError>().expect("parse error")
}
pub fn as_validate_error(error: anyhow::Error) -> ValidateError {
error.downcast::<ValidateError>().expect("validate error")
}
macro_rules! parse_test {
($parse_output:ident, $data:expr, $result:tt, $check_impl:block) => {{
let data = $data;
fn check_by_ref<'a>(
$result: Result<
($parse_output<ByRef<&'a [u8]>>, ByRef<&'a [u8]>),
<$parse_output<ByRef<&'a [u8]>> as crate::Parse<ByRef<&'a [u8]>>>::Error,
>,
) {
$check_impl;
}
fn check_by_value(
$result: Result<
($parse_output<ByValue<Vec<u8>>>, ByValue<Vec<u8>>),
<$parse_output<ByValue<Vec<u8>>> as crate::Parse<ByValue<Vec<u8>>>>::Error,
>,
) -> Option<($parse_output<ByValue<Vec<u8>>>, ByValue<Vec<u8>>)> {
$check_impl
}
let by_ref = ByRef::new(data.as_slice());
let by_ref_result = $parse_output::parse(by_ref);
check_by_ref(by_ref_result);
let by_value_result = $parse_output::<ByValue<Vec<u8>>>::parse(ByValue::new(data));
let _ = check_by_value(by_value_result);
}};
}
pub(crate) use parse_test;
macro_rules! validate_test {
($parse_output:ident, $data:expr, $result:tt, $check_impl:block) => {{
let data = $data;
fn check_by_ref<'a>(
$result: Result<(), <$parse_output<ByRef<&'a [u8]>> as crate::Validate>::Error>,
) {
$check_impl;
}
fn check_by_value(
$result: Result<(), <$parse_output<ByValue<Vec<u8>>> as crate::Validate>::Error>,
) {
$check_impl
}
let by_ref = ByRef::new(data.as_slice());
let (by_ref_parsed, _) =
$parse_output::parse(by_ref).expect("successful parse for validate test");
let by_ref_result = by_ref_parsed.validate();
check_by_ref(by_ref_result);
let (by_value_parsed, _) = $parse_output::<ByValue<Vec<u8>>>::parse(ByValue::new(data))
.expect("successful parse for validate test");
let by_value_result = by_value_parsed.validate();
check_by_value(by_value_result);
}};
}
pub(crate) use validate_test;
}