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#[derive(Debug)]
90pub struct CAllocatedBlock<T> {
91 pub(crate) pointer: ptr::NonNull<T>,
92 _phantom_data: PhantomData<T>,
93}
94
95unsafe 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 #[must_use]
112 pub fn as_ptr(&self) -> *const T {
113 self.pointer.as_ptr()
114 }
115
116 #[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 #[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#[derive(Debug)]
141pub(crate) struct OptionalNativeFunctions {
142 pub(crate) security_reject_unknown: unsafe extern "C" fn() -> c_int,
144
145 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 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 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 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 pub(crate) selinux_flush_class_cache: unsafe extern "C" fn(),
179
180 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 pub(crate) selinux_restorecon_get_skipped_errors: unsafe extern "C" fn() -> c_ulong,
189
190 pub(crate) getpidprevcon:
192 unsafe extern "C" fn(pid: selinux_sys::pid_t, con: *mut *mut c_char) -> c_int,
193
194 pub(crate) getpidprevcon_raw:
196 unsafe extern "C" fn(pid: selinux_sys::pid_t, con: *mut *mut c_char) -> c_int,
197
198 pub(crate) selinux_restorecon_get_relabeled_files: unsafe extern "C" fn() -> c_ulong,
200}
201
202pub(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 unsafe { selinux_sys::is_selinux_enabled() };
244
245 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}