#[cfg(test)]
mod tests;
use alloc::ffi::CString;
use core::ffi::CStr;
use core::marker::PhantomData;
use core::{mem, ptr};
use std::ffi::OsStr;
use std::io;
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
use std::path::{Path, PathBuf};
use once_cell::sync::OnceCell;
use crate::errors::{Error, Result};
pub(crate) fn str_to_c_string(the_str: &str) -> Result<CString> {
CString::new(the_str).map_err(|_r| Error::IO1Name {
operation: "CString::new",
name: the_str.into(),
source: io::ErrorKind::InvalidInput.into(),
})
}
#[cfg(unix)]
pub(crate) fn os_str_to_c_string(os_str: &OsStr) -> Result<CString> {
use std::os::unix::ffi::OsStrExt;
CString::new(os_str.as_bytes()).map_err(|_r| Error::PathIsInvalid(PathBuf::from(os_str)))
}
pub(crate) fn c_str_ptr_to_str<'string>(ptr: *const c_char) -> Result<&'string str> {
if ptr.is_null() {
let err = io::ErrorKind::InvalidInput.into();
Err(Error::from_io("utils::c_str_ptr_to_string()", err))
} else {
unsafe { CStr::from_ptr(ptr) }.to_str().map_err(Into::into)
}
}
#[cfg(unix)]
pub(crate) fn c_str_ptr_to_path<'string>(path_ptr: *const c_char) -> &'string Path {
use std::os::unix::ffi::OsStrExt;
let c_path = unsafe { CStr::from_ptr(path_ptr) };
Path::new(OsStr::from_bytes(c_path.to_bytes()))
}
pub(crate) fn c_str_to_non_null_ptr(c_str: &CStr) -> ptr::NonNull<c_char> {
unsafe { ptr::NonNull::new_unchecked(c_str.as_ptr().cast_mut()) }
}
pub(crate) fn get_static_path(
proc: unsafe extern "C" fn() -> *const c_char,
proc_name: &'static str,
) -> Result<&'static Path> {
let path_ptr = unsafe { proc() };
if path_ptr.is_null() {
Err(Error::from_io(proc_name, io::ErrorKind::InvalidData.into()))
} else {
Ok(c_str_ptr_to_path(path_ptr))
}
}
pub(crate) fn ret_val_to_result(proc_name: &'static str, result: c_int) -> Result<()> {
if result == -1_i32 {
Err(Error::last_io_error(proc_name))
} else {
Ok(())
}
}
pub(crate) fn ret_val_to_result_with_path(
proc_name: &'static str,
result: c_int,
path: &Path,
) -> Result<()> {
if result == -1_i32 {
let err = io::Error::last_os_error();
Err(Error::from_io_path(proc_name, path, err))
} else {
Ok(())
}
}
#[derive(Debug)]
pub struct CAllocatedBlock<T> {
pub(crate) pointer: ptr::NonNull<T>,
_phantom_data: PhantomData<T>,
}
unsafe impl<T> Send for CAllocatedBlock<T> {}
impl<T> CAllocatedBlock<T> {
pub(crate) fn new(pointer: *mut T) -> Option<Self> {
ptr::NonNull::new(pointer).map(|pointer| Self {
pointer,
_phantom_data: PhantomData,
})
}
#[must_use]
pub fn as_ptr(&self) -> *const T {
self.pointer.as_ptr()
}
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.pointer.as_ptr()
}
}
impl CAllocatedBlock<c_char> {
#[must_use]
pub fn as_c_str(&self) -> &CStr {
unsafe { CStr::from_ptr(self.pointer.as_ptr()) }
}
}
impl<T> Drop for CAllocatedBlock<T> {
fn drop(&mut self) {
let pointer = self.pointer.as_ptr();
self.pointer = ptr::NonNull::dangling();
unsafe { libc::free(pointer.cast()) };
}
}
#[derive(Debug)]
pub(crate) struct OptionalNativeFunctions {
pub(crate) security_reject_unknown: unsafe extern "C" fn() -> c_int,
pub(crate) selabel_get_digests_all_partial_matches: unsafe extern "C" fn(
rec: *mut selinux_sys::selabel_handle,
key: *const c_char,
calculated_digest: *mut *mut u8,
xattr_digest: *mut *mut u8,
digest_len: *mut usize,
) -> bool,
pub(crate) selabel_hash_all_partial_matches: unsafe extern "C" fn(
rec: *mut selinux_sys::selabel_handle,
key: *const c_char,
digest: *mut u8,
) -> bool,
pub(crate) security_validatetrans: unsafe extern "C" fn(
scon: *const c_char,
tcon: *const c_char,
tclass: selinux_sys::security_class_t,
newcon: *const c_char,
) -> c_int,
pub(crate) security_validatetrans_raw: unsafe extern "C" fn(
scon: *const c_char,
tcon: *const c_char,
tclass: selinux_sys::security_class_t,
newcon: *const c_char,
) -> c_int,
pub(crate) selinux_flush_class_cache: unsafe extern "C" fn(),
pub(crate) selinux_restorecon_parallel: unsafe extern "C" fn(
pathname: *const c_char,
restorecon_flags: c_uint,
nthreads: usize,
) -> c_int,
pub(crate) selinux_restorecon_get_skipped_errors: unsafe extern "C" fn() -> c_ulong,
pub(crate) getpidprevcon:
unsafe extern "C" fn(pid: selinux_sys::pid_t, con: *mut *mut c_char) -> c_int,
pub(crate) getpidprevcon_raw:
unsafe extern "C" fn(pid: selinux_sys::pid_t, con: *mut *mut c_char) -> c_int,
pub(crate) selinux_restorecon_get_relabeled_files: unsafe extern "C" fn() -> c_ulong,
}
pub(crate) static OPT_NATIVE_FN: OnceCell<OptionalNativeFunctions> = OnceCell::new();
impl Default for OptionalNativeFunctions {
fn default() -> Self {
Self {
security_reject_unknown: Self::not_impl_security_reject_unknown,
selabel_get_digests_all_partial_matches:
Self::not_impl_selabel_get_digests_all_partial_matches,
selabel_hash_all_partial_matches: Self::not_impl_selabel_hash_all_partial_matches,
security_validatetrans: Self::not_impl_security_validatetrans,
security_validatetrans_raw: Self::not_impl_security_validatetrans,
selinux_flush_class_cache: Self::not_impl_selinux_flush_class_cache,
selinux_restorecon_parallel: Self::not_impl_selinux_restorecon_parallel,
selinux_restorecon_get_skipped_errors:
Self::not_impl_selinux_restorecon_get_skipped_errors,
getpidprevcon: Self::not_impl_getpidprevcon,
getpidprevcon_raw: Self::not_impl_getpidprevcon,
selinux_restorecon_get_relabeled_files:
Self::not_impl_selinux_restorecon_get_relabeled_files,
}
}
}
impl OptionalNativeFunctions {
pub(crate) fn get() -> &'static Self {
OPT_NATIVE_FN.get_or_init(Self::initialize)
}
fn initialize() -> Self {
let mut r = Self::default();
let lib_handle = Self::get_libselinux_handle();
if !lib_handle.is_null() {
r.load_functions_addresses(lib_handle);
}
Error::clear_errno();
r
}
fn get_libselinux_handle() -> *mut c_void {
unsafe { selinux_sys::is_selinux_enabled() };
let flags = libc::RTLD_NOW | libc::RTLD_GLOBAL | libc::RTLD_NOLOAD | libc::RTLD_NODELETE;
for &lib_name in &[
c"libselinux.so.1",
c"libselinux.so",
c"libselinux",
c"selinux",
] {
let lib_handle = unsafe { libc::dlopen(lib_name.as_ptr(), flags) };
if !lib_handle.is_null() {
return lib_handle;
}
}
ptr::null_mut()
}
#[allow(clippy::missing_transmute_annotations)]
fn load_functions_addresses(&mut self, lib_handle: *mut c_void) {
let f = unsafe { libc::dlsym(lib_handle, c"security_reject_unknown".as_ptr()) };
if !f.is_null() {
self.security_reject_unknown = unsafe { mem::transmute(f) };
}
let c_name = c"selabel_get_digests_all_partial_matches";
let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
if !f.is_null() {
self.selabel_get_digests_all_partial_matches = unsafe { mem::transmute(f) };
}
let c_name = c"selabel_hash_all_partial_matches";
let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
if !f.is_null() {
self.selabel_hash_all_partial_matches = unsafe { mem::transmute(f) };
}
let f = unsafe { libc::dlsym(lib_handle, c"security_validatetrans".as_ptr()) };
if !f.is_null() {
self.security_validatetrans = unsafe { mem::transmute(f) };
}
let f = unsafe { libc::dlsym(lib_handle, c"security_validatetrans_raw".as_ptr()) };
if !f.is_null() {
self.security_validatetrans_raw = unsafe { mem::transmute(f) };
}
let f = unsafe { libc::dlsym(lib_handle, c"selinux_flush_class_cache".as_ptr()) };
if !f.is_null() {
self.selinux_flush_class_cache = unsafe { mem::transmute(f) };
}
let f = unsafe { libc::dlsym(lib_handle, c"selinux_restorecon_parallel".as_ptr()) };
if !f.is_null() {
self.selinux_restorecon_parallel = unsafe { mem::transmute(f) };
}
let c_name = c"selinux_restorecon_get_skipped_errors";
let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
if !f.is_null() {
self.selinux_restorecon_get_skipped_errors = unsafe { mem::transmute(f) };
}
let f = unsafe { libc::dlsym(lib_handle, c"getpidprevcon".as_ptr()) };
if !f.is_null() {
self.getpidprevcon = unsafe { mem::transmute(f) };
}
let f = unsafe { libc::dlsym(lib_handle, c"getpidprevcon_raw".as_ptr()) };
if !f.is_null() {
self.getpidprevcon_raw = unsafe { mem::transmute(f) };
}
let c_name = c"selinux_restorecon_get_relabeled_files";
let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
if !f.is_null() {
self.selinux_restorecon_get_relabeled_files = unsafe { mem::transmute(f) };
}
}
unsafe extern "C" fn not_impl_security_reject_unknown() -> c_int {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1_i32
}
unsafe extern "C" fn not_impl_selabel_get_digests_all_partial_matches(
_rec: *mut selinux_sys::selabel_handle,
_key: *const c_char,
_calculated_digest: *mut *mut u8,
_xattr_digest: *mut *mut u8,
_digest_len: *mut usize,
) -> bool {
errno::set_errno(errno::Errno(libc::ENOSYS));
false
}
unsafe extern "C" fn not_impl_selabel_hash_all_partial_matches(
_rec: *mut selinux_sys::selabel_handle,
_key: *const c_char,
_digest: *mut u8,
) -> bool {
errno::set_errno(errno::Errno(libc::ENOSYS));
false
}
unsafe extern "C" fn not_impl_security_validatetrans(
_scon: *const c_char,
_tcon: *const c_char,
_tclass: selinux_sys::security_class_t,
_newcon: *const c_char,
) -> c_int {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1_i32
}
unsafe extern "C" fn not_impl_selinux_flush_class_cache() {
errno::set_errno(errno::Errno(libc::ENOSYS));
}
unsafe extern "C" fn not_impl_selinux_restorecon_parallel(
_pathname: *const c_char,
_restorecon_flags: c_uint,
_nthreads: usize,
) -> c_int {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1_i32
}
unsafe extern "C" fn not_impl_selinux_restorecon_get_skipped_errors() -> c_ulong {
0
}
unsafe extern "C" fn not_impl_getpidprevcon(
_pid: selinux_sys::pid_t,
_con: *mut *mut c_char,
) -> c_int {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1_i32
}
unsafe extern "C" fn not_impl_selinux_restorecon_get_relabeled_files() -> c_ulong {
errno::set_errno(errno::Errno(libc::ENOSYS));
0
}
}