Skip to main content

selinux/utils/
mod.rs

1#[cfg(test)]
2mod tests;
3
4use alloc::ffi::CString;
5use core::ffi::CStr;
6use core::marker::PhantomData;
7use core::{mem, ptr};
8use std::ffi::OsStr;
9use std::io;
10use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
11use std::path::{Path, PathBuf};
12
13use once_cell::sync::OnceCell;
14
15use crate::errors::{Error, Result};
16
17pub(crate) fn str_to_c_string(the_str: &str) -> Result<CString> {
18    CString::new(the_str).map_err(|_r| Error::IO1Name {
19        operation: "CString::new",
20        name: the_str.into(),
21        source: io::ErrorKind::InvalidInput.into(),
22    })
23}
24
25#[cfg(unix)]
26pub(crate) fn os_str_to_c_string(os_str: &OsStr) -> Result<CString> {
27    use std::os::unix::ffi::OsStrExt;
28
29    CString::new(os_str.as_bytes()).map_err(|_r| Error::PathIsInvalid(PathBuf::from(os_str)))
30}
31
32pub(crate) fn c_str_ptr_to_str<'string>(ptr: *const c_char) -> Result<&'string str> {
33    if ptr.is_null() {
34        let err = io::ErrorKind::InvalidInput.into();
35        Err(Error::from_io("utils::c_str_ptr_to_string()", err))
36    } else {
37        unsafe { CStr::from_ptr(ptr) }.to_str().map_err(Into::into)
38    }
39}
40
41#[cfg(unix)]
42pub(crate) fn c_str_ptr_to_path<'string>(path_ptr: *const c_char) -> &'string Path {
43    use std::os::unix::ffi::OsStrExt;
44
45    let c_path = unsafe { CStr::from_ptr(path_ptr) };
46    Path::new(OsStr::from_bytes(c_path.to_bytes()))
47}
48
49pub(crate) fn c_str_to_non_null_ptr(c_str: &CStr) -> ptr::NonNull<c_char> {
50    unsafe { ptr::NonNull::new_unchecked(c_str.as_ptr().cast_mut()) }
51}
52
53pub(crate) fn get_static_path(
54    proc: unsafe extern "C" fn() -> *const c_char,
55    proc_name: &'static str,
56) -> Result<&'static Path> {
57    let path_ptr = unsafe { proc() };
58    if path_ptr.is_null() {
59        Err(Error::from_io(proc_name, io::ErrorKind::InvalidData.into()))
60    } else {
61        Ok(c_str_ptr_to_path(path_ptr))
62    }
63}
64
65pub(crate) fn ret_val_to_result(proc_name: &'static str, result: c_int) -> Result<()> {
66    if result == -1_i32 {
67        Err(Error::last_io_error(proc_name))
68    } else {
69        Ok(())
70    }
71}
72
73pub(crate) fn ret_val_to_result_with_path(
74    proc_name: &'static str,
75    result: c_int,
76    path: &Path,
77) -> Result<()> {
78    if result == -1_i32 {
79        let err = io::Error::last_os_error();
80        Err(Error::from_io_path(proc_name, path, err))
81    } else {
82        Ok(())
83    }
84}
85
86/// An owned block of memory, allocated with [`libc::malloc`].
87///
88/// Dropping this instance calls [`libc::free`] on the managed pointer.
89#[derive(Debug)]
90pub struct CAllocatedBlock<T> {
91    pub(crate) pointer: ptr::NonNull<T>,
92    _phantom_data: PhantomData<T>,
93}
94
95/// # Safety
96///
97/// - [`libc::malloc()`]-allocated memory blocks are accessible from any thread.
98/// - [`libc::free()`] supports deallocating memory blocks allocated in
99///   a different thread.
100unsafe impl<T> Send for CAllocatedBlock<T> {}
101
102impl<T> CAllocatedBlock<T> {
103    pub(crate) fn new(pointer: *mut T) -> Option<Self> {
104        ptr::NonNull::new(pointer).map(|pointer| Self {
105            pointer,
106            _phantom_data: PhantomData,
107        })
108    }
109
110    /// Return the managed raw pointer.
111    #[must_use]
112    pub fn as_ptr(&self) -> *const T {
113        self.pointer.as_ptr()
114    }
115
116    /// Return the managed raw pointer.
117    #[must_use]
118    pub fn as_mut_ptr(&mut self) -> *mut T {
119        self.pointer.as_ptr()
120    }
121}
122
123impl CAllocatedBlock<c_char> {
124    /// Return the managed null-terminated C string.
125    #[must_use]
126    pub fn as_c_str(&self) -> &CStr {
127        unsafe { CStr::from_ptr(self.pointer.as_ptr()) }
128    }
129}
130
131impl<T> Drop for CAllocatedBlock<T> {
132    fn drop(&mut self) {
133        let pointer = self.pointer.as_ptr();
134        self.pointer = ptr::NonNull::dangling();
135        unsafe { libc::free(pointer.cast()) };
136    }
137}
138
139/// Holds addresses of `libselinux`'s optionally-implemented functions.
140#[derive(Debug)]
141pub(crate) struct OptionalNativeFunctions {
142    /// Since version 2.9
143    pub(crate) security_reject_unknown: unsafe extern "C" fn() -> c_int,
144
145    /// Since version 3.0
146    pub(crate) selabel_get_digests_all_partial_matches: unsafe extern "C" fn(
147        rec: *mut selinux_sys::selabel_handle,
148        key: *const c_char,
149        calculated_digest: *mut *mut u8,
150        xattr_digest: *mut *mut u8,
151        digest_len: *mut usize,
152    ) -> bool,
153
154    /// Since version 3.0
155    pub(crate) selabel_hash_all_partial_matches: unsafe extern "C" fn(
156        rec: *mut selinux_sys::selabel_handle,
157        key: *const c_char,
158        digest: *mut u8,
159    ) -> bool,
160
161    /// Since version 3.0
162    pub(crate) security_validatetrans: unsafe extern "C" fn(
163        scon: *const c_char,
164        tcon: *const c_char,
165        tclass: selinux_sys::security_class_t,
166        newcon: *const c_char,
167    ) -> c_int,
168
169    /// Since version 3.0
170    pub(crate) security_validatetrans_raw: unsafe extern "C" fn(
171        scon: *const c_char,
172        tcon: *const c_char,
173        tclass: selinux_sys::security_class_t,
174        newcon: *const c_char,
175    ) -> c_int,
176
177    /// Since version 3.1
178    pub(crate) selinux_flush_class_cache: unsafe extern "C" fn(),
179
180    /// Since version 3.4
181    pub(crate) selinux_restorecon_parallel: unsafe extern "C" fn(
182        pathname: *const c_char,
183        restorecon_flags: c_uint,
184        nthreads: usize,
185    ) -> c_int,
186
187    /// Since version 3.4
188    pub(crate) selinux_restorecon_get_skipped_errors: unsafe extern "C" fn() -> c_ulong,
189
190    /// Since version 3.5
191    pub(crate) getpidprevcon:
192        unsafe extern "C" fn(pid: selinux_sys::pid_t, con: *mut *mut c_char) -> c_int,
193
194    /// Since version 3.5
195    pub(crate) getpidprevcon_raw:
196        unsafe extern "C" fn(pid: selinux_sys::pid_t, con: *mut *mut c_char) -> c_int,
197
198    /// Since version 3.10
199    pub(crate) selinux_restorecon_get_relabeled_files: unsafe extern "C" fn() -> c_ulong,
200}
201
202/// Addresses of optionally-implemented functions by libselinux.
203pub(crate) static OPT_NATIVE_FN: OnceCell<OptionalNativeFunctions> = OnceCell::new();
204
205impl Default for OptionalNativeFunctions {
206    fn default() -> Self {
207        Self {
208            security_reject_unknown: Self::not_impl_security_reject_unknown,
209            selabel_get_digests_all_partial_matches:
210                Self::not_impl_selabel_get_digests_all_partial_matches,
211            selabel_hash_all_partial_matches: Self::not_impl_selabel_hash_all_partial_matches,
212            security_validatetrans: Self::not_impl_security_validatetrans,
213            security_validatetrans_raw: Self::not_impl_security_validatetrans,
214            selinux_flush_class_cache: Self::not_impl_selinux_flush_class_cache,
215            selinux_restorecon_parallel: Self::not_impl_selinux_restorecon_parallel,
216            selinux_restorecon_get_skipped_errors:
217                Self::not_impl_selinux_restorecon_get_skipped_errors,
218            getpidprevcon: Self::not_impl_getpidprevcon,
219            getpidprevcon_raw: Self::not_impl_getpidprevcon,
220            selinux_restorecon_get_relabeled_files:
221                Self::not_impl_selinux_restorecon_get_relabeled_files,
222        }
223    }
224}
225
226impl OptionalNativeFunctions {
227    pub(crate) fn get() -> &'static Self {
228        OPT_NATIVE_FN.get_or_init(Self::initialize)
229    }
230
231    fn initialize() -> Self {
232        let mut r = Self::default();
233        let lib_handle = Self::get_libselinux_handle();
234        if !lib_handle.is_null() {
235            r.load_functions_addresses(lib_handle);
236        }
237        Error::clear_errno();
238        r
239    }
240
241    fn get_libselinux_handle() -> *mut c_void {
242        // Ensure libselinux is loaded.
243        unsafe { selinux_sys::is_selinux_enabled() };
244
245        // Get a handle to the already-loaded libselinux.
246        let flags = libc::RTLD_NOW | libc::RTLD_GLOBAL | libc::RTLD_NOLOAD | libc::RTLD_NODELETE;
247        for &lib_name in &[
248            c"libselinux.so.1",
249            c"libselinux.so",
250            c"libselinux",
251            c"selinux",
252        ] {
253            let lib_handle = unsafe { libc::dlopen(lib_name.as_ptr(), flags) };
254            if !lib_handle.is_null() {
255                return lib_handle;
256            }
257        }
258        ptr::null_mut()
259    }
260
261    #[allow(clippy::missing_transmute_annotations)]
262    fn load_functions_addresses(&mut self, lib_handle: *mut c_void) {
263        let f = unsafe { libc::dlsym(lib_handle, c"security_reject_unknown".as_ptr()) };
264        if !f.is_null() {
265            self.security_reject_unknown = unsafe { mem::transmute(f) };
266        }
267
268        let c_name = c"selabel_get_digests_all_partial_matches";
269        let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
270        if !f.is_null() {
271            self.selabel_get_digests_all_partial_matches = unsafe { mem::transmute(f) };
272        }
273
274        let c_name = c"selabel_hash_all_partial_matches";
275        let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
276        if !f.is_null() {
277            self.selabel_hash_all_partial_matches = unsafe { mem::transmute(f) };
278        }
279
280        let f = unsafe { libc::dlsym(lib_handle, c"security_validatetrans".as_ptr()) };
281        if !f.is_null() {
282            self.security_validatetrans = unsafe { mem::transmute(f) };
283        }
284
285        let f = unsafe { libc::dlsym(lib_handle, c"security_validatetrans_raw".as_ptr()) };
286        if !f.is_null() {
287            self.security_validatetrans_raw = unsafe { mem::transmute(f) };
288        }
289
290        let f = unsafe { libc::dlsym(lib_handle, c"selinux_flush_class_cache".as_ptr()) };
291        if !f.is_null() {
292            self.selinux_flush_class_cache = unsafe { mem::transmute(f) };
293        }
294
295        let f = unsafe { libc::dlsym(lib_handle, c"selinux_restorecon_parallel".as_ptr()) };
296        if !f.is_null() {
297            self.selinux_restorecon_parallel = unsafe { mem::transmute(f) };
298        }
299
300        let c_name = c"selinux_restorecon_get_skipped_errors";
301        let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
302        if !f.is_null() {
303            self.selinux_restorecon_get_skipped_errors = unsafe { mem::transmute(f) };
304        }
305
306        let f = unsafe { libc::dlsym(lib_handle, c"getpidprevcon".as_ptr()) };
307        if !f.is_null() {
308            self.getpidprevcon = unsafe { mem::transmute(f) };
309        }
310
311        let f = unsafe { libc::dlsym(lib_handle, c"getpidprevcon_raw".as_ptr()) };
312        if !f.is_null() {
313            self.getpidprevcon_raw = unsafe { mem::transmute(f) };
314        }
315
316        let c_name = c"selinux_restorecon_get_relabeled_files";
317        let f = unsafe { libc::dlsym(lib_handle, c_name.as_ptr()) };
318        if !f.is_null() {
319            self.selinux_restorecon_get_relabeled_files = unsafe { mem::transmute(f) };
320        }
321    }
322
323    unsafe extern "C" fn not_impl_security_reject_unknown() -> c_int {
324        errno::set_errno(errno::Errno(libc::ENOSYS));
325        -1_i32
326    }
327
328    unsafe extern "C" fn not_impl_selabel_get_digests_all_partial_matches(
329        _rec: *mut selinux_sys::selabel_handle,
330        _key: *const c_char,
331        _calculated_digest: *mut *mut u8,
332        _xattr_digest: *mut *mut u8,
333        _digest_len: *mut usize,
334    ) -> bool {
335        errno::set_errno(errno::Errno(libc::ENOSYS));
336        false
337    }
338
339    unsafe extern "C" fn not_impl_selabel_hash_all_partial_matches(
340        _rec: *mut selinux_sys::selabel_handle,
341        _key: *const c_char,
342        _digest: *mut u8,
343    ) -> bool {
344        errno::set_errno(errno::Errno(libc::ENOSYS));
345        false
346    }
347
348    unsafe extern "C" fn not_impl_security_validatetrans(
349        _scon: *const c_char,
350        _tcon: *const c_char,
351        _tclass: selinux_sys::security_class_t,
352        _newcon: *const c_char,
353    ) -> c_int {
354        errno::set_errno(errno::Errno(libc::ENOSYS));
355        -1_i32
356    }
357
358    unsafe extern "C" fn not_impl_selinux_flush_class_cache() {
359        errno::set_errno(errno::Errno(libc::ENOSYS));
360    }
361
362    unsafe extern "C" fn not_impl_selinux_restorecon_parallel(
363        _pathname: *const c_char,
364        _restorecon_flags: c_uint,
365        _nthreads: usize,
366    ) -> c_int {
367        errno::set_errno(errno::Errno(libc::ENOSYS));
368        -1_i32
369    }
370
371    unsafe extern "C" fn not_impl_selinux_restorecon_get_skipped_errors() -> c_ulong {
372        0
373    }
374
375    unsafe extern "C" fn not_impl_getpidprevcon(
376        _pid: selinux_sys::pid_t,
377        _con: *mut *mut c_char,
378    ) -> c_int {
379        errno::set_errno(errno::Errno(libc::ENOSYS));
380        -1_i32
381    }
382
383    unsafe extern "C" fn not_impl_selinux_restorecon_get_relabeled_files() -> c_ulong {
384        errno::set_errno(errno::Errno(libc::ENOSYS));
385        0
386    }
387}