#[cfg(test)]
mod tests;
use alloc::sync::{Arc, Weak};
use core::marker::PhantomData;
use core::ptr;
use std::io;
use std::os::raw::{c_char, c_int, c_uint, c_void};
use parking_lot::Mutex;
use crate::SecurityContext;
use crate::errors::{Error, Result};
use crate::utils::{ret_val_to_result, str_to_c_string};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
struct SELinuxOption(selinux_sys::selinux_opt);
unsafe impl Send for SELinuxOption {}
unsafe impl Sync for SELinuxOption {}
#[derive(Debug, PartialEq, Eq)]
pub struct AccessVectorCache {
options: Box<[SELinuxOption]>,
}
impl AccessVectorCache {
#[doc(alias = "avc_open")]
pub fn initialize(options: &[(c_int, *const c_void)]) -> Result<Arc<Self>> {
static AVC: Mutex<Weak<AccessVectorCache>> = Mutex::new(Weak::new());
let mut options: Vec<SELinuxOption> = options
.iter()
.map(|&(type_, value)| {
SELinuxOption(selinux_sys::selinux_opt {
type_,
value: value.cast(),
})
})
.collect();
options.sort_unstable();
options.dedup();
let mut options = options.into_boxed_slice();
let count = c_uint::try_from(options.len())?;
let options_ptr = if count == 0 {
ptr::null_mut()
} else {
options.as_mut_ptr().cast()
};
let mut avc = AVC.lock();
if let Some(existing_avc) = Weak::upgrade(&*avc) {
if *existing_avc.options == *options {
Ok(existing_avc)
} else {
let err = io::ErrorKind::AlreadyExists.into();
Err(Error::from_io("AccessVectorCache::initialize()", err))
}
} else {
if unsafe { selinux_sys::avc_open(options_ptr, count) } == -1 {
Err(Error::last_io_error("avc_open()"))
} else {
let new_avc = Arc::new(AccessVectorCache { options });
*avc = Arc::downgrade(&new_avc);
Ok(new_avc)
}
}
}
#[doc(alias = "avc_reset")]
pub fn reset(&self) -> Result<()> {
ret_val_to_result("avc_reset()", unsafe { selinux_sys::avc_reset() })
}
#[doc(alias = "avc_cleanup")]
pub fn clean_up(&self) {
unsafe { selinux_sys::avc_cleanup() }
}
#[doc(alias = "avc_get_initial_sid")]
pub fn kernel_initial_security_id<'context>(
&'context self,
security_id_name: &str,
raw_format: bool,
) -> Result<SecurityID<'context>> {
let c_name = str_to_c_string(security_id_name)?;
let mut security_id: *mut selinux_sys::security_id = ptr::null_mut();
if unsafe { selinux_sys::avc_get_initial_sid(c_name.as_ptr(), &raw mut security_id) }
== -1_i32
{
Err(Error::last_io_error("avc_get_initial_sid()"))
} else {
Ok(SecurityID {
security_id,
is_raw: raw_format,
_phantom_data: PhantomData,
})
}
}
#[doc(alias = "avc_sid_to_context")]
pub fn security_context_from_security_id<'context>(
&'context self,
mut security_id: SecurityID,
) -> Result<SecurityContext<'context>> {
let is_raw = security_id.is_raw_format();
let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if is_raw {
let proc_name = "avc_sid_to_context_raw()";
(selinux_sys::avc_sid_to_context_raw, proc_name)
} else {
let proc_name = "avc_sid_to_context()";
(selinux_sys::avc_sid_to_context, proc_name)
};
let mut context: *mut c_char = ptr::null_mut();
let r = unsafe { proc(security_id.as_mut_ptr(), &raw mut context) };
SecurityContext::from_result(proc_name, r, context, is_raw)
}
#[doc(alias = "avc_context_to_sid")]
pub fn security_id_from_security_context<'context>(
&'context self,
context: SecurityContext,
) -> Result<SecurityID<'context>> {
let is_raw = context.is_raw_format();
let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if is_raw {
let proc_name = "avc_context_to_sid_raw()";
(selinux_sys::avc_context_to_sid_raw, proc_name)
} else {
let proc_name = "avc_context_to_sid()";
(selinux_sys::avc_context_to_sid, proc_name)
};
let mut security_id: *mut selinux_sys::security_id = ptr::null_mut();
if unsafe { proc(context.as_ptr(), &raw mut security_id) } == -1_i32 {
Err(Error::last_io_error(proc_name))
} else {
Ok(SecurityID {
security_id,
is_raw,
_phantom_data: PhantomData,
})
}
}
}
impl Drop for AccessVectorCache {
fn drop(&mut self) {
unsafe { selinux_sys::avc_destroy() };
}
}
#[derive(Debug)]
pub struct SecurityID<'id> {
security_id: *mut selinux_sys::security_id,
is_raw: bool,
_phantom_data: PhantomData<&'id selinux_sys::security_id>,
}
impl SecurityID<'_> {
#[must_use]
pub fn is_unspecified(&self) -> bool {
self.security_id.is_null()
}
#[must_use]
pub fn is_raw_format(&self) -> bool {
self.is_raw
}
#[must_use]
pub fn as_ptr(&self) -> *const selinux_sys::security_id {
self.security_id.cast()
}
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut selinux_sys::security_id {
self.security_id
}
}
impl Default for SecurityID<'_> {
fn default() -> Self {
Self {
security_id: ptr::null_mut(),
is_raw: false,
_phantom_data: PhantomData,
}
}
}