blob: 189266a06ee43c3ff7a5be2b309e1d25c984e77a [file] [log] [blame]
#[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 reference_counted_singleton::{RCSRef, RefCountedSingleton};
use parking_lot::Mutex;
use crate::errors::{Error, Result};
use crate::utils::{ret_val_to_result, str_to_c_string};
use crate::SecurityContext;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
struct SELinuxOption(selinux_sys::selinux_opt);
// SAFETY: `selinux_sys::selinux_opt` is treated as an opaque byte sequence.
// In particular, the pointer inside `selinux_sys::selinux_opt` is never dereferenced.
unsafe impl Send for SELinuxOption {}
unsafe impl Sync for SELinuxOption {}
/// Access vector cache.
#[derive(Debug, PartialEq, Eq)]
pub struct AccessVectorCache {
options: Box<[SELinuxOption]>,
}
impl AccessVectorCache {
/// Initialize the user space access vector cache.
///
/// The `options` parameter produces zero or more `(type, value)` tuples, where:
/// - `type` is one of `selinux_sys::AVC_OPT_*` values,
/// *e.g.*, [`selinux_sys::AVC_OPT_SETENFORCE`].
/// - `value` is a pointer whose semantics are specific to `type`.
///
/// Attempting to initialize the access vector cache while it is still
/// initialized succeeds only if the subsequent initialization uses the same
/// set of options as the previous, still in scope, one.
///
/// See: `avc_open()`.
#[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 {
// Initializing, while still initialized, using the same set of options.
Ok(existing_avc)
} else {
// Initializing, while still initialized, with a different set of options,
// is an error.
let err = io::ErrorKind::AlreadyExists.into();
Err(Error::from_io("AccessVectorCache::initialize()", err))
}
} else {
// First initialization, or reinitialization.
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)
}
}
}
/// Flush the user space access vector cache, causing it to forget any
/// cached access decisions.
///
/// See: `avc_reset()`.
#[doc(alias = "avc_reset")]
pub fn reset(&self) -> Result<()> {
ret_val_to_result("avc_reset()", unsafe { selinux_sys::avc_reset() })
}
/// Attempt to free unused memory within the user space access vector
/// cache, but do not flush any cached access decisions.
///
/// See: `avc_cleanup()`.
#[doc(alias = "avc_cleanup")]
pub fn clean_up(&self) {
unsafe { selinux_sys::avc_cleanup() }
}
/// Return a security identifier for the kernel initial security identifier
/// specified by `security_identifier_name`.
///
/// See: `avc_get_initial_sid()`.
#[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(), &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,
})
}
}
/// Return a security context for the given security identifier.
///
/// See: `avc_sid_to_context()`.
#[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(), &mut context) };
SecurityContext::from_result(proc_name, r, context, is_raw)
}
/// Return a security identifier for the given security context.
///
/// See: `avc_context_to_sid()`.
#[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(), &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() };
}
}
/// SELinux security identifier.
#[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<'_> {
/// Return `true` if the security identifier is unspecified.
#[must_use]
pub fn is_unspecified(&self) -> bool {
self.security_id.is_null()
}
/// Return `false` if security context translation must be performed.
#[must_use]
pub fn is_raw_format(&self) -> bool {
self.is_raw
}
/// Return the managed raw pointer to [`selinux_sys::security_id`].
#[must_use]
pub fn as_ptr(&self) -> *const selinux_sys::security_id {
self.security_id.cast()
}
/// Return the managed raw pointer to [`selinux_sys::security_id`].
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut selinux_sys::security_id {
self.security_id
}
}
impl Default for SecurityID<'_> {
/// Return an unspecified security identifier.
fn default() -> Self {
Self {
security_id: ptr::null_mut(),
is_raw: false,
_phantom_data: PhantomData,
}
}
}