| #![cfg(target_os = "linux")] |
| #![doc = include_str!("../README.md")] |
| #![doc(html_root_url = "https://docs.rs/selinux/0.4.6")] |
| #![allow(clippy::upper_case_acronyms)] |
| #![warn( |
| unsafe_op_in_unsafe_fn, |
| missing_docs, |
| clippy::must_use_candidate, |
| clippy::default_numeric_fallback, |
| clippy::single_char_lifetime_names, |
| clippy::alloc_instead_of_core, |
| clippy::std_instead_of_core, |
| clippy::std_instead_of_alloc |
| )] |
| // |
| // https://rust-lang.github.io/api-guidelines/checklist.html |
| // |
| |
| // Activate these lints to clean up the code and hopefully detect some issues. |
| //#![warn(clippy::all, clippy::pedantic, clippy::restriction)] |
| #![allow( |
| clippy::absolute_paths, |
| clippy::arbitrary_source_item_ordering, |
| clippy::doc_markdown, |
| clippy::error_impl_error, |
| clippy::exhaustive_structs, |
| clippy::expect_used, |
| clippy::field_scoped_visibility_modifiers, |
| clippy::impl_trait_in_params, |
| clippy::implicit_return, |
| clippy::indexing_slicing, |
| clippy::min_ident_chars, |
| clippy::missing_docs_in_private_items, |
| clippy::missing_errors_doc, |
| clippy::missing_inline_in_public_items, |
| clippy::missing_trait_methods, |
| clippy::mod_module_files, |
| clippy::module_name_repetitions, |
| clippy::multiple_unsafe_ops_per_block, |
| clippy::needless_pass_by_value, |
| clippy::pattern_type_mismatch, |
| clippy::pub_use, |
| clippy::pub_with_shorthand, |
| clippy::question_mark_used, |
| clippy::separated_literal_suffix, |
| clippy::shadow_reuse, |
| clippy::shadow_unrelated, |
| clippy::single_call_fn, |
| clippy::undocumented_unsafe_blocks, |
| clippy::unused_self, |
| clippy::unused_trait_names |
| )] |
| #![cfg_attr(test, allow(clippy::unwrap_used))] |
| |
| #[cfg(test)] |
| mod tests; |
| |
| /// Access Vector Cache. |
| pub mod avc; |
| /// SELinux call backs. |
| pub mod call_back; |
| /// Restore file(s) default SELinux security contexts. |
| pub mod context_restore; |
| /// Errors. |
| pub mod errors; |
| /// Labeling files. |
| pub mod label; |
| /// SELinux paths. |
| pub mod path; |
| /// SELinux policies. |
| pub mod policy; |
| /// Utilities. |
| pub mod utils; |
| |
| extern crate alloc; |
| |
| use alloc::borrow::Cow; |
| use alloc::ffi::CString; |
| use core::ffi::CStr; |
| use core::marker::PhantomData; |
| use core::mem::MaybeUninit; |
| use core::{cmp, fmt, mem, ptr, slice, str}; |
| use std::collections::{hash_map, HashMap}; |
| use std::io; |
| use std::os::raw::{c_char, c_int, c_uint, c_void}; |
| use std::os::unix::io::AsRawFd; |
| use std::path::Path; |
| |
| use selinux_sys::pid_t; |
| |
| #[macro_use] |
| extern crate bitflags; |
| |
| use errors::{Error, Result}; |
| use utils::{ |
| c_str_to_non_null_ptr, os_str_to_c_string, ret_val_to_result, ret_val_to_result_with_path, |
| str_to_c_string, CAllocatedBlock, OptionalNativeFunctions, |
| }; |
| |
| /// Red, green and blue components of a color. |
| #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub struct RGB { |
| /// Red component. |
| pub red: u8, |
| /// Green component. |
| pub green: u8, |
| /// Blue component. |
| pub blue: u8, |
| } |
| |
| impl RGB { |
| /// Create a new instance. |
| #[must_use] |
| pub fn new(red: u8, green: u8, blue: u8) -> Self { |
| Self { red, green, blue } |
| } |
| } |
| |
| /// Background and foreground colors. |
| #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub struct LayerColors { |
| /// Background color. |
| pub background: RGB, |
| /// Foreground color. |
| pub foreground: RGB, |
| } |
| |
| impl LayerColors { |
| /// Create a new instance. |
| #[must_use] |
| pub fn new(background: RGB, foreground: RGB) -> Self { |
| Self { |
| background, |
| foreground, |
| } |
| } |
| } |
| |
| /// Colors of a security context. |
| #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub struct SecurityContextColors { |
| /// Background and foreground colors of SELinux user. |
| pub user: LayerColors, |
| /// Background and foreground colors of SELinux role. |
| pub role: LayerColors, |
| /// Background and foreground colors of SELinux type. |
| pub the_type: LayerColors, |
| /// Background and foreground colors of SELinux range. |
| pub range: LayerColors, |
| } |
| |
| impl SecurityContextColors { |
| /// Create a new instance. |
| #[must_use] |
| pub fn new( |
| user: LayerColors, |
| role: LayerColors, |
| the_type: LayerColors, |
| range: LayerColors, |
| ) -> Self { |
| Self { |
| user, |
| role, |
| the_type, |
| range, |
| } |
| } |
| } |
| |
| /// SELinux security context. |
| #[derive(Debug)] |
| pub struct SecurityContext<'context> { |
| context: ptr::NonNull<c_char>, |
| size: Option<usize>, |
| is_raw: bool, |
| context_owned: bool, |
| _phantom_data: PhantomData<&'context c_char>, |
| } |
| |
| impl<'context> SecurityContext<'context> { |
| /// 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 [`c_char`]. |
| #[must_use] |
| pub fn as_ptr(&self) -> *const c_char { |
| self.context.as_ptr() |
| } |
| |
| /// Return the managed raw pointer to [`c_char`]. |
| #[must_use] |
| pub fn as_mut_ptr(&mut self) -> *mut c_char { |
| self.context.as_ptr() |
| } |
| |
| /// Return the security context's byte slice. |
| #[must_use] |
| pub fn as_bytes(&self) -> &[u8] { |
| self.size.map_or_else( |
| || unsafe { CStr::from_ptr(self.context.as_ptr()).to_bytes() }, |
| |size| unsafe { slice::from_raw_parts(self.context.as_ptr().cast(), size) }, |
| ) |
| } |
| |
| /// Return the string value of this security context. |
| /// |
| /// If the context is empty, then this returns `Ok(None)`. |
| pub fn to_c_string(&self) -> Result<Option<Cow<CStr>>> { |
| if let Some(size) = self.size { |
| let bytes = unsafe { slice::from_raw_parts(self.context.as_ptr().cast(), size) }; |
| if bytes.is_empty() { |
| Ok(None) |
| } else if bytes.last().copied() == Some(0) { |
| if let Ok(result) = CStr::from_bytes_with_nul(bytes) { |
| Ok(Some(Cow::Borrowed(result))) |
| } else { |
| let op = "CStr::from_bytes_with_nul()"; |
| Err(Error::from_io(op, io::ErrorKind::InvalidData.into())) |
| } |
| } else if let Ok(result) = CString::new(bytes) { |
| Ok(Some(Cow::Owned(result))) |
| } else { |
| let op = "CString::new()"; |
| Err(Error::from_io(op, io::ErrorKind::InvalidData.into())) |
| } |
| } else { |
| let result = unsafe { CStr::from_ptr(self.context.as_ptr()) }; |
| Ok(Some(Cow::Borrowed(result))) |
| } |
| } |
| |
| /// Return the security context identified by `context`. |
| /// |
| /// ⚠️ The returned instance does **NOT** own the provided context. |
| /// When the returned instance get dropped, it will **NOT** deallocate |
| /// the provided context. |
| #[must_use] |
| pub fn from_c_str(c_context: &'context CStr, raw_format: bool) -> SecurityContext<'context> { |
| Self { |
| context: c_str_to_non_null_ptr(c_context), |
| size: Some(c_context.to_bytes().len()), |
| is_raw: raw_format, |
| context_owned: false, |
| _phantom_data: PhantomData, |
| } |
| } |
| |
| /// Return the security context of the current process. |
| /// |
| /// See: `getcon()`. |
| #[doc(alias = "getcon")] |
| pub fn current(raw_format: bool) -> Result<Self> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::getcon_raw, "getcon_raw()") |
| } else { |
| (selinux_sys::getcon, "getcon()") |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { proc(&mut context) }; |
| Self::from_result(proc_name, r, context, raw_format) |
| } |
| |
| /// Return the security context of the current process before the last exec. |
| /// |
| /// See: `getprevcon()`. |
| #[doc(alias = "getprevcon")] |
| pub fn previous(raw_format: bool) -> Result<Self> { |
| Self::previous_of_process(None, raw_format) |
| } |
| |
| /// Return the security context, of the current or specified process, before the last exec. |
| /// |
| /// If `process_id` is `None`, then the current process is queried. |
| /// |
| /// Specifying a particular process id (`process_id.is_some()`) requires `libselinux` version |
| /// `3.5` or later. |
| /// |
| /// See: `getprevcon()`, `getpidprevcon()`. |
| #[doc(alias = "getprevcon")] |
| #[doc(alias = "getpidprevcon")] |
| pub fn previous_of_process(process_id: Option<pid_t>, raw_format: bool) -> Result<Self> { |
| let mut context: *mut c_char = ptr::null_mut(); |
| |
| if let Some(process_id) = process_id { |
| let onf = OptionalNativeFunctions::get(); |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if raw_format { |
| (onf.getpidprevcon_raw, "getpidprevcon_raw()") |
| } else { |
| (onf.getpidprevcon, "getpidprevcon()") |
| }; |
| |
| let r = unsafe { proc(process_id, &mut context) }; |
| Self::from_result_with_pid(proc_name, r, context, process_id, raw_format) |
| } else { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::getprevcon_raw, "getprevcon_raw()") |
| } else { |
| (selinux_sys::getprevcon, "getprevcon()") |
| }; |
| |
| let r = unsafe { proc(&mut context) }; |
| Self::from_result(proc_name, r, context, raw_format) |
| } |
| } |
| |
| /// Set the current security context of the process to this context. |
| /// |
| /// See: `setcon()`. |
| #[doc(alias = "setcon")] |
| pub fn set_as_current(&self) -> Result<()> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if self.is_raw { |
| (selinux_sys::setcon_raw, "setcon_raw()") |
| } else { |
| (selinux_sys::setcon, "setcon()") |
| }; |
| |
| ret_val_to_result(proc_name, unsafe { proc(self.context.as_ptr()) }) |
| } |
| |
| /// Get the context of a kernel initial security identifier specified by name. |
| /// |
| /// See: `security_get_initial_context()`. |
| #[doc(alias = "security_get_initial_context")] |
| pub fn of_initial_kernel_context(name: &str, raw_format: bool) -> Result<Self> { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if raw_format { |
| let proc_name = "security_get_initial_context_raw()"; |
| (selinux_sys::security_get_initial_context_raw, proc_name) |
| } else { |
| let proc_name = "security_get_initial_context()"; |
| (selinux_sys::security_get_initial_context, proc_name) |
| }; |
| |
| let c_name = str_to_c_string(name)?; |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { proc(c_name.as_ptr(), &mut context) }; |
| Self::from_result_with_name(proc_name, r, context, name, raw_format) |
| } |
| |
| /// Get the default SELinux security context for the specified media type |
| /// from the policy. |
| /// |
| /// See: `matchmediacon()`. |
| #[doc(alias = "matchmediacon")] |
| pub fn of_media_type(name: &str) -> Result<Self> { |
| let c_name = str_to_c_string(name)?; |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { selinux_sys::matchmediacon(c_name.as_ptr(), &mut context) }; |
| Self::from_result_with_name("matchmediacon()", r, context, name, false) |
| } |
| |
| /// Return the process context for the specified process identifier. |
| /// |
| /// See: `getpidcon()`. |
| #[doc(alias = "getpidcon")] |
| pub fn of_process(process_id: pid_t, raw_format: bool) -> Result<Self> { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if raw_format { |
| (selinux_sys::getpidcon_raw, "getpidcon_raw()") |
| } else { |
| (selinux_sys::getpidcon, "getpidcon()") |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { proc(process_id, &mut context) }; |
| Self::from_result_with_pid(proc_name, r, context, process_id, raw_format) |
| } |
| |
| /// Perform context translation from the human-readable format (translated) |
| /// to the internal system format (raw). |
| /// |
| /// See: `selinux_trans_to_raw_context()`. |
| #[doc(alias = "selinux_trans_to_raw_context")] |
| pub fn to_raw_format(&self) -> Result<Self> { |
| if self.is_raw { |
| return Err(Error::UnexpectedSecurityContextFormat); |
| } |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { |
| selinux_sys::selinux_trans_to_raw_context(self.context.as_ptr(), &mut context) |
| }; |
| Self::from_result("selinux_trans_to_raw_context()", r, context, true) |
| } |
| |
| /// Perform context translation from the internal system format (raw) to |
| /// the human-readable format (translated). |
| /// |
| /// See: `selinux_raw_to_trans_context()`. |
| #[doc(alias = "selinux_raw_to_trans_context")] |
| pub fn to_translated_format(&self) -> Result<Self> { |
| if !self.is_raw { |
| return Err(Error::UnexpectedSecurityContextFormat); |
| } |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { |
| selinux_sys::selinux_raw_to_trans_context(self.context.as_ptr(), &mut context) |
| }; |
| Self::from_result("selinux_raw_to_trans_context()", r, context, false) |
| } |
| |
| /// Ask the user to manually enter a context as a fallback if a list of |
| /// authorized contexts could not be obtained. |
| /// |
| /// See: `manual_user_enter_context()`. |
| #[doc(alias = "manual_user_enter_context")] |
| pub fn of_se_user_with_selected_context(se_user: &str, raw_format: bool) -> Result<Self> { |
| let mut context: *mut c_char = ptr::null_mut(); |
| let c_se_user = str_to_c_string(se_user)?; |
| let r = unsafe { selinux_sys::manual_user_enter_context(c_se_user.as_ptr(), &mut context) }; |
| Self::from_result("manual_user_enter_context()", r, context, raw_format) |
| } |
| |
| /// Obtain a context, for the specified SELinux user identity, that is |
| /// reachable from the specified `reachable_from_context`. |
| /// |
| /// See: `get_default_context()`, `get_default_context_with_level()`, |
| /// `get_default_context_with_role()`, `get_default_context_with_rolelevel()`. |
| #[doc(alias = "get_default_context")] |
| #[doc(alias = "get_default_context_with_level")] |
| #[doc(alias = "get_default_context_with_role")] |
| #[doc(alias = "get_default_context_with_rolelevel")] |
| pub fn default_for_se_user( |
| se_user: &str, |
| role: Option<&str>, |
| level: Option<&str>, |
| reachable_from_context: Option<&Self>, |
| raw_format: bool, |
| ) -> Result<Self> { |
| let c_se_user = str_to_c_string(se_user)?; |
| |
| let c_role = if let Some(role) = role { |
| str_to_c_string(role).map(Some)? |
| } else { |
| None |
| }; |
| |
| let c_level = if let Some(level) = level { |
| str_to_c_string(level).map(Some)? |
| } else { |
| None |
| }; |
| |
| let reachable_from_context = |
| reachable_from_context.map_or(ptr::null_mut(), |c| c.context.as_ptr()); |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| |
| let (r, proc_name) = unsafe { |
| match (c_role, c_level) { |
| (None, None) => ( |
| selinux_sys::get_default_context( |
| c_se_user.as_ptr(), |
| reachable_from_context, |
| &mut context, |
| ), |
| "get_default_context()", |
| ), |
| |
| (None, Some(c_level)) => ( |
| selinux_sys::get_default_context_with_level( |
| c_se_user.as_ptr(), |
| c_level.as_ptr(), |
| reachable_from_context, |
| &mut context, |
| ), |
| "get_default_context_with_level()", |
| ), |
| |
| (Some(c_role), None) => ( |
| selinux_sys::get_default_context_with_role( |
| c_se_user.as_ptr(), |
| c_role.as_ptr(), |
| reachable_from_context, |
| &mut context, |
| ), |
| "get_default_context_with_role()", |
| ), |
| |
| (Some(c_role), Some(c_level)) => ( |
| selinux_sys::get_default_context_with_rolelevel( |
| c_se_user.as_ptr(), |
| c_role.as_ptr(), |
| c_level.as_ptr(), |
| reachable_from_context, |
| &mut context, |
| ), |
| "get_default_context_with_rolelevel()", |
| ), |
| } |
| }; |
| |
| Self::from_result(proc_name, r, context, raw_format) |
| } |
| |
| /// Get the context used for executing a new process. |
| /// |
| /// See: `getexeccon()`. |
| #[doc(alias = "getexeccon")] |
| pub fn of_next_exec(raw_format: bool) -> Result<Option<Self>> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::getexeccon_raw, "getexeccon_raw()") |
| } else { |
| (selinux_sys::getexeccon, "getexeccon()") |
| }; |
| |
| Self::of_new_operations(proc, proc_name, raw_format) |
| } |
| |
| /// Reset the context, used for the next `execve()` call, to the default |
| /// policy behavior. |
| /// |
| /// See: `setexeccon()`. |
| #[doc(alias = "setexeccon")] |
| pub fn set_default_context_for_next_exec() -> Result<()> { |
| let r = unsafe { selinux_sys::setexeccon(ptr::null()) }; |
| ret_val_to_result("setexeccon()", r) |
| } |
| |
| /// Set the context used for the next `execve()` call. |
| /// |
| /// See: `setexeccon()`. |
| #[doc(alias = "setexeccon")] |
| pub fn set_for_next_exec(&self) -> Result<()> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if self.is_raw { |
| (selinux_sys::setexeccon_raw, "setexeccon_raw()") |
| } else { |
| (selinux_sys::setexeccon, "setexeccon()") |
| }; |
| |
| ret_val_to_result(proc_name, unsafe { proc(self.context.as_ptr()) }) |
| } |
| |
| /// Get the context used for creating a new file system object. |
| /// |
| /// See: `getfscreatecon()`. |
| #[doc(alias = "getfscreatecon")] |
| pub fn of_new_file_system_objects(raw_format: bool) -> Result<Option<Self>> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::getfscreatecon_raw, "getfscreatecon_raw()") |
| } else { |
| (selinux_sys::getfscreatecon, "getfscreatecon()") |
| }; |
| |
| Self::of_new_operations(proc, proc_name, raw_format) |
| } |
| |
| /// Reset the context, used for creating a new file system object, to the |
| /// default policy behavior. |
| /// |
| /// See: `setfscreatecon()`. |
| #[doc(alias = "setfscreatecon")] |
| pub fn set_default_context_for_new_file_system_objects() -> Result<()> { |
| let r = unsafe { selinux_sys::setfscreatecon(ptr::null()) }; |
| ret_val_to_result("setfscreatecon()", r) |
| } |
| |
| /// Set the context used for creating a new file system object. |
| /// |
| /// See: `setfscreatecon()`. |
| #[doc(alias = "setfscreatecon")] |
| pub fn set_for_new_file_system_objects(&self, raw_format: bool) -> Result<()> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::setfscreatecon_raw, "setfscreatecon_raw()") |
| } else { |
| (selinux_sys::setfscreatecon, "setfscreatecon()") |
| }; |
| |
| ret_val_to_result(proc_name, unsafe { proc(self.context.as_ptr()) }) |
| } |
| |
| /// Get the context used for creating a new kernel key ring. |
| /// |
| /// See: `getkeycreatecon()`. |
| #[doc(alias = "getkeycreatecon")] |
| pub fn of_new_kernel_key_rings(raw_format: bool) -> Result<Option<Self>> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::getkeycreatecon_raw, "getkeycreatecon_raw()") |
| } else { |
| (selinux_sys::getkeycreatecon, "getkeycreatecon()") |
| }; |
| |
| Self::of_new_operations(proc, proc_name, raw_format) |
| } |
| |
| /// Set the context, used for creating a new kernel key ring, to the |
| /// default policy behavior. |
| /// |
| /// See: `setkeycreatecon()`. |
| #[doc(alias = "setkeycreatecon")] |
| pub fn set_default_context_for_new_kernel_key_rings() -> Result<()> { |
| let r = unsafe { selinux_sys::setkeycreatecon(ptr::null()) }; |
| ret_val_to_result("setkeycreatecon()", r) |
| } |
| |
| /// Set the context used for creating a new kernel key ring. |
| /// |
| /// See: `setkeycreatecon()`. |
| #[doc(alias = "setkeycreatecon")] |
| pub fn set_for_new_kernel_key_rings(&self, raw_format: bool) -> Result<()> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::setkeycreatecon_raw, "setkeycreatecon_raw()") |
| } else { |
| (selinux_sys::setkeycreatecon, "setkeycreatecon()") |
| }; |
| |
| ret_val_to_result(proc_name, unsafe { proc(self.context.as_ptr()) }) |
| } |
| |
| /// Get the context used for creating a new labeled network socket. |
| /// |
| /// See: `getsockcreatecon()`. |
| #[doc(alias = "getsockcreatecon")] |
| pub fn of_new_labeled_sockets(raw_format: bool) -> Result<Option<Self>> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::getsockcreatecon_raw, "getsockcreatecon_raw()") |
| } else { |
| (selinux_sys::getsockcreatecon, "getsockcreatecon()") |
| }; |
| |
| Self::of_new_operations(proc, proc_name, raw_format) |
| } |
| |
| /// Set the context, used for creating a new labeled network sockets, to the |
| /// default policy behavior. |
| /// |
| /// See: `setsockcreatecon()`. |
| #[doc(alias = "setsockcreatecon")] |
| pub fn set_default_context_for_new_labeled_sockets() -> Result<()> { |
| let r = unsafe { selinux_sys::setsockcreatecon(ptr::null()) }; |
| ret_val_to_result("setsockcreatecon()", r) |
| } |
| |
| /// Set the context used for creating a new labeled network sockets. |
| /// |
| /// See: `setsockcreatecon()`. |
| #[doc(alias = "setsockcreatecon")] |
| pub fn set_for_new_labeled_sockets(&self, raw_format: bool) -> Result<()> { |
| let (proc, proc_name): (unsafe extern "C" fn(_) -> _, _) = if raw_format { |
| (selinux_sys::setsockcreatecon_raw, "setsockcreatecon_raw()") |
| } else { |
| (selinux_sys::setsockcreatecon, "setsockcreatecon()") |
| }; |
| |
| ret_val_to_result(proc_name, unsafe { proc(self.context.as_ptr()) }) |
| } |
| |
| /// Get the context associated with the given path in the file system. |
| /// |
| /// See: `lgetfilecon()`, `getfilecon()`. |
| #[doc(alias = "lgetfilecon")] |
| #[doc(alias = "getfilecon")] |
| pub fn of_path( |
| path: impl AsRef<Path>, |
| follow_symbolic_links: bool, |
| raw_format: bool, |
| ) -> Result<Option<Self>> { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = |
| match (follow_symbolic_links, raw_format) { |
| (false, false) => (selinux_sys::lgetfilecon, "lgetfilecon()"), |
| (false, true) => (selinux_sys::lgetfilecon_raw, "lgetfilecon_raw()"), |
| (true, false) => (selinux_sys::getfilecon, "getfilecon()"), |
| (true, true) => (selinux_sys::getfilecon_raw, "getfilecon_raw()"), |
| }; |
| |
| let c_path = os_str_to_c_string(path.as_ref().as_os_str())?; |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r: c_int = unsafe { proc(c_path.as_ptr(), &mut context) }; |
| if r == -1_i32 { |
| let err = io::Error::last_os_error(); |
| if let Some(libc::ENODATA) = err.raw_os_error() { |
| Ok(None) |
| } else { |
| Err(Error::from_io_path(proc_name, path.as_ref(), err)) |
| } |
| } else { |
| Ok(ptr::NonNull::new(context).map(|context| { |
| let size = (r >= 0_i32).then_some(r as c_uint); |
| Self::from_ptr(context, size, raw_format) |
| })) |
| } |
| } |
| |
| /// Set the file context to the system defaults. |
| /// |
| /// See: `selinux_lsetfilecon_default()`. |
| #[doc(alias = "selinux_lsetfilecon_default")] |
| pub fn set_default_for_path(path: impl AsRef<Path>) -> Result<()> { |
| let c_path = os_str_to_c_string(path.as_ref().as_os_str())?; |
| let r = unsafe { selinux_sys::selinux_lsetfilecon_default(c_path.as_ptr()) }; |
| ret_val_to_result_with_path("selinux_lsetfilecon_default()", r, path.as_ref()) |
| } |
| |
| /// Set the SELinux security context of a file system object. |
| /// |
| /// See: `lsetfilecon()`, `setfilecon()`. |
| #[doc(alias = "lsetfilecon")] |
| #[doc(alias = "setfilecon")] |
| pub fn set_for_path( |
| &self, |
| path: impl AsRef<Path>, |
| follow_symbolic_links: bool, |
| raw_format: bool, |
| ) -> Result<()> { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = |
| match (follow_symbolic_links, raw_format) { |
| (false, false) => (selinux_sys::lsetfilecon, "lsetfilecon()"), |
| (false, true) => (selinux_sys::lsetfilecon_raw, "lsetfilecon_raw()"), |
| (true, false) => (selinux_sys::setfilecon, "setfilecon()"), |
| (true, true) => (selinux_sys::setfilecon_raw, "setfilecon_raw()"), |
| }; |
| |
| let c_path = os_str_to_c_string(path.as_ref().as_os_str())?; |
| let r = unsafe { proc(c_path.as_ptr(), self.context.as_ptr()) }; |
| ret_val_to_result_with_path(proc_name, r, path.as_ref()) |
| } |
| |
| /// Get the SELinux security context of a file system object. |
| /// |
| /// See: `fgetfilecon()`. |
| #[doc(alias = "fgetfilecon")] |
| pub fn of_file<T>(fd: &T, raw_format: bool) -> Result<Option<Self>> |
| where |
| T: AsRawFd, |
| { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if raw_format { |
| (selinux_sys::fgetfilecon_raw, "fgetfilecon_raw()") |
| } else { |
| (selinux_sys::fgetfilecon, "fgetfilecon()") |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r: c_int = unsafe { proc(fd.as_raw_fd(), &mut context) }; |
| if r == -1_i32 { |
| let err = io::Error::last_os_error(); |
| if let Some(libc::ENODATA) = err.raw_os_error() { |
| Ok(None) |
| } else { |
| Err(Error::from_io(proc_name, err)) |
| } |
| } else { |
| Ok(ptr::NonNull::new(context).map(|context| { |
| #[allow(clippy::cast_sign_loss, clippy::as_conversions)] |
| let size = (r >= 0_i32).then_some(r as c_uint); |
| Self::from_ptr(context, size, raw_format) |
| })) |
| } |
| } |
| |
| /// Set the SELinux security context of the file system object identified |
| /// by an open file descriptor. |
| /// |
| /// See: `fsetfilecon()`. |
| #[doc(alias = "fsetfilecon")] |
| pub fn set_for_file<T>(&self, fd: &T) -> Result<()> |
| where |
| T: AsRawFd, |
| { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if self.is_raw { |
| (selinux_sys::fsetfilecon_raw, "fsetfilecon_raw()") |
| } else { |
| (selinux_sys::fsetfilecon, "fsetfilecon()") |
| }; |
| |
| let r = unsafe { proc(fd.as_raw_fd(), self.context.as_ptr()) }; |
| ret_val_to_result(proc_name, r) |
| } |
| |
| /// Set the SELinux security context of the peer socket identified by an |
| /// open file descriptor. |
| /// |
| /// See: `getpeercon()`. |
| #[doc(alias = "getpeercon")] |
| pub fn of_peer_socket<T>(socket: &T, raw_format: bool) -> Result<Self> |
| where |
| T: AsRawFd, |
| { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if raw_format { |
| (selinux_sys::getpeercon_raw, "getpeercon_raw()") |
| } else { |
| (selinux_sys::getpeercon, "getpeercon()") |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { proc(socket.as_raw_fd(), &mut context) }; |
| Self::from_result(proc_name, r, context, raw_format) |
| } |
| |
| /// Return whether the policy permits this source context to access |
| /// `target_context` via `target_class` with the requested access vector. |
| /// |
| /// See: `security_compute_av_flags()`. |
| #[doc(alias = "security_compute_av_flags")] |
| pub fn query_access_decision( |
| &self, |
| target_context: &Self, |
| target_class: SecurityClass, |
| requested_access: selinux_sys::access_vector_t, |
| ) -> Result<selinux_sys::av_decision> { |
| if self.is_raw != target_context.is_raw { |
| return Err(Error::SecurityContextFormatMismatch); |
| } |
| |
| let (proc, proc_name): (unsafe extern "C" fn(_, _, _, _, _) -> _, _) = if self.is_raw { |
| let proc_name = "security_compute_av_flags_raw()"; |
| (selinux_sys::security_compute_av_flags_raw, proc_name) |
| } else { |
| let proc_name = "security_compute_av_flags()"; |
| (selinux_sys::security_compute_av_flags, proc_name) |
| }; |
| |
| let mut result = MaybeUninit::<selinux_sys::av_decision>::uninit(); |
| let r: c_int = unsafe { |
| proc( |
| self.context.as_ptr(), |
| target_context.context.as_ptr(), |
| target_class.0, |
| requested_access, |
| result.as_mut_ptr(), |
| ) |
| }; |
| |
| if r == -1_i32 { |
| Err(Error::last_io_error(proc_name)) |
| } else { |
| Ok(unsafe { result.assume_init() }) |
| } |
| } |
| |
| /// Compute a context to use for labeling a new named object in a particular |
| /// class based on a SID pair. |
| /// |
| /// See: `security_compute_create_name()`. |
| #[doc(alias = "security_compute_create_name")] |
| pub fn of_labeling_decision( |
| &self, |
| target_context: &Self, |
| target_class: SecurityClass, |
| object_name: &str, |
| ) -> Result<Self> { |
| if self.is_raw != target_context.is_raw { |
| return Err(Error::SecurityContextFormatMismatch); |
| } |
| |
| let (proc, proc_name): (unsafe extern "C" fn(_, _, _, _, _) -> _, _) = if self.is_raw { |
| let proc_name = "security_compute_create_name_raw()"; |
| (selinux_sys::security_compute_create_name_raw, proc_name) |
| } else { |
| let proc_name = "security_compute_create_name()"; |
| (selinux_sys::security_compute_create_name, proc_name) |
| }; |
| |
| let c_object_name = str_to_c_string(object_name)?; |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { |
| proc( |
| self.context.as_ptr(), |
| target_context.context.as_ptr(), |
| target_class.0, |
| c_object_name.as_ptr(), |
| &mut context, |
| ) |
| }; |
| |
| Self::from_result_with_name(proc_name, r, context, object_name, self.is_raw) |
| } |
| |
| /// Compute the new context to use when relabeling an object. |
| /// |
| /// See: `security_compute_relabel()`. |
| #[doc(alias = "security_compute_relabel")] |
| pub fn of_relabeling_decision( |
| &self, |
| target_context: &Self, |
| target_class: SecurityClass, |
| ) -> Result<Self> { |
| if self.is_raw != target_context.is_raw { |
| return Err(Error::SecurityContextFormatMismatch); |
| } |
| |
| let (proc, proc_name): (unsafe extern "C" fn(_, _, _, _) -> _, _) = if self.is_raw { |
| let proc_name = "security_compute_relabel_raw()"; |
| (selinux_sys::security_compute_relabel_raw, proc_name) |
| } else { |
| let proc_name = "security_compute_relabel()"; |
| (selinux_sys::security_compute_relabel, proc_name) |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { |
| proc( |
| self.context.as_ptr(), |
| target_context.context.as_ptr(), |
| target_class.0, |
| &mut context, |
| ) |
| }; |
| |
| Self::from_result(proc_name, r, context, self.is_raw) |
| } |
| |
| /// Compute the context to use when labeling a polyinstantiated |
| /// object instance. |
| /// |
| /// See: `security_compute_member()`. |
| #[doc(alias = "security_compute_member")] |
| pub fn of_polyinstantiation_member_decision( |
| &self, |
| target_context: &Self, |
| target_class: SecurityClass, |
| ) -> Result<Self> { |
| if self.is_raw != target_context.is_raw { |
| return Err(Error::SecurityContextFormatMismatch); |
| } |
| |
| let (proc, proc_name): (unsafe extern "C" fn(_, _, _, _) -> _, _) = if self.is_raw { |
| let proc_name = "security_compute_member_raw()"; |
| (selinux_sys::security_compute_member_raw, proc_name) |
| } else { |
| let proc_name = "security_compute_member()"; |
| (selinux_sys::security_compute_member, proc_name) |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { |
| proc( |
| self.context.as_ptr(), |
| target_context.context.as_ptr(), |
| target_class.0, |
| &mut context, |
| ) |
| }; |
| |
| Self::from_result(proc_name, r, context, self.is_raw) |
| } |
| |
| /// Determine if a transition from this context to `new_context` using |
| /// `target_context` as the object is valid for object class `target_class`. |
| /// |
| /// This checks against the `mlsvalidatetrans` and `validatetrans` |
| /// constraints in the loaded policy. |
| /// |
| /// This function requires `libselinux` version `3.0` or later. |
| /// |
| /// See: `security_validatetrans()`. |
| #[doc(alias = "security_validatetrans")] |
| pub fn validate_transition( |
| &self, |
| target_context: &Self, |
| target_class: SecurityClass, |
| new_context: &Self, |
| ) -> Result<()> { |
| if self.is_raw != target_context.is_raw { |
| return Err(Error::SecurityContextFormatMismatch); |
| } |
| |
| let onf = OptionalNativeFunctions::get(); |
| let (proc, proc_name) = if self.is_raw { |
| let proc_name = "security_validatetrans_raw()"; |
| (onf.security_validatetrans_raw, proc_name) |
| } else { |
| let proc_name = "security_validatetrans()"; |
| (onf.security_validatetrans, proc_name) |
| }; |
| |
| let r = unsafe { |
| proc( |
| self.context.as_ptr(), |
| target_context.context.as_ptr(), |
| target_class.0, |
| new_context.context.as_ptr(), |
| ) |
| }; |
| |
| ret_val_to_result(proc_name, r) |
| } |
| |
| /// Check the validity of an SELinux context. |
| /// |
| /// See: `security_check_context()`, `is_selinux_enabled()`. |
| #[doc(alias = "security_check_context")] |
| #[doc(alias = "is_selinux_enabled")] |
| #[must_use] |
| pub fn check(&self) -> Option<bool> { |
| let proc: unsafe extern "C" fn(_) -> _ = if self.is_raw { |
| selinux_sys::security_check_context_raw |
| } else { |
| selinux_sys::security_check_context |
| }; |
| |
| if unsafe { proc(self.context.as_ptr()) } == -1_i32 { |
| if unsafe { selinux_sys::is_selinux_enabled() } == 0_i32 { |
| None |
| } else { |
| Some(false) |
| } |
| } else { |
| Some(true) |
| } |
| } |
| |
| /// Canonicalize this security context. |
| /// |
| /// See: `security_canonicalize_context()`. |
| #[doc(alias = "security_canonicalize_context")] |
| pub fn canonicalize(&self) -> Result<Self> { |
| let (proc, proc_name): (unsafe extern "C" fn(_, _) -> _, _) = if self.is_raw { |
| let proc_name = "security_canonicalize_context_raw()"; |
| (selinux_sys::security_canonicalize_context_raw, proc_name) |
| } else { |
| let proc_name = "security_canonicalize_context()"; |
| (selinux_sys::security_canonicalize_context, proc_name) |
| }; |
| |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = unsafe { proc(self.context.as_ptr(), &mut context) }; |
| Self::from_result(proc_name, r, context, self.is_raw) |
| } |
| |
| /// Check if this context has the access permission for the specified class |
| /// on the target context. |
| /// |
| /// See: `selinux_check_access()`. |
| #[doc(alias = "selinux_check_access")] |
| #[allow(clippy::not_unsafe_ptr_arg_deref)] |
| pub fn check_access( |
| &self, |
| target_context: &Self, |
| target_class: &str, |
| requested_permission: &str, |
| audit_data: *mut c_void, |
| ) -> Result<bool> { |
| let c_target_class = str_to_c_string(target_class)?; |
| let c_requested_permission = str_to_c_string(requested_permission)?; |
| |
| let r: c_int = unsafe { |
| selinux_sys::selinux_check_access( |
| self.context.as_ptr(), |
| target_context.context.as_ptr(), |
| c_target_class.as_ptr(), |
| c_requested_permission.as_ptr(), |
| audit_data, |
| ) |
| }; |
| |
| Ok(r == 0_i32) |
| } |
| |
| /// Check whether a SELinux tty security context is defined as |
| /// a securetty context. |
| /// |
| /// See: `selinux_check_securetty_context()`. |
| #[doc(alias = "selinux_check_securetty_context")] |
| #[must_use] |
| pub fn check_securetty_context(&self) -> bool { |
| unsafe { selinux_sys::selinux_check_securetty_context(self.context.as_ptr()) == 0_i32 } |
| } |
| |
| /// Check whether SELinux context type is customizable by the administrator. |
| /// |
| /// See: `is_context_customizable()`. |
| #[doc(alias = "is_context_customizable")] |
| pub fn is_customizable(&self) -> Result<bool> { |
| let r: c_int = unsafe { selinux_sys::is_context_customizable(self.context.as_ptr()) }; |
| if r == -1_i32 { |
| Err(Error::last_io_error("is_context_customizable()")) |
| } else { |
| Ok(r != 0_i32) |
| } |
| } |
| |
| /// Return the color string for this SELinux security context. |
| /// |
| /// See: `selinux_raw_context_to_color()`. |
| #[doc(alias = "selinux_raw_context_to_color")] |
| pub fn to_color(&self) -> Result<SecurityContextColors> { |
| if !self.is_raw { |
| let raw_context = self.to_raw_format()?; |
| return raw_context.to_color(); |
| } |
| |
| let mut color_ptr: *mut c_char = ptr::null_mut(); |
| let r: c_int = unsafe { |
| selinux_sys::selinux_raw_context_to_color(self.context.as_ptr(), &mut color_ptr) |
| }; |
| |
| if r == -1_i32 { |
| Err(Error::last_io_error("selinux_raw_context_to_color()")) |
| } else { |
| CAllocatedBlock::new(color_ptr).map_or_else( |
| || { |
| let err = io::ErrorKind::InvalidData.into(); |
| Err(Error::from_io("selinux_raw_context_to_color()", err)) |
| }, |
| |c_color| Self::parse_context_color(c_color.as_c_str().to_bytes()), |
| ) |
| } |
| } |
| |
| /// Compare this SELinux security context with another one, excluding |
| /// the `user` component. |
| /// |
| /// See: `selinux_file_context_cmp()`. |
| #[doc(alias = "selinux_file_context_cmp")] |
| #[must_use] |
| pub fn compare_user_insensitive(&self, other: &Self) -> cmp::Ordering { |
| let r: c_int = unsafe { |
| selinux_sys::selinux_file_context_cmp(self.context.as_ptr(), other.context.as_ptr()) |
| }; |
| |
| r.cmp(&0_i32) |
| } |
| |
| /// Compare the SELinux security context on disk to the default security |
| /// context required by the policy file contexts file. |
| /// |
| /// See: `selinux_file_context_verify()`. |
| #[doc(alias = "selinux_file_context_verify")] |
| pub fn verify_file_context( |
| path: impl AsRef<Path>, |
| mode: Option<FileAccessMode>, |
| ) -> Result<bool> { |
| let c_path = os_str_to_c_string(path.as_ref().as_os_str())?; |
| let mode = mode.map_or(0, FileAccessMode::mode); |
| |
| Error::clear_errno(); |
| |
| match unsafe { selinux_sys::selinux_file_context_verify(c_path.as_ptr(), mode) } { |
| -1_i32 => Err(Error::from_io_path( |
| "selinux_file_context_verify()", |
| path.as_ref(), |
| io::Error::last_os_error(), |
| )), |
| |
| 0_i32 => { |
| let err = io::Error::last_os_error(); |
| match err.raw_os_error() { |
| None | Some(0_i32) => Ok(false), |
| |
| _ => Err(Error::from_io_path( |
| "selinux_file_context_verify()", |
| path.as_ref(), |
| err, |
| )), |
| } |
| } |
| |
| _ => Ok(true), |
| } |
| } |
| |
| fn of_new_operations( |
| proc: unsafe extern "C" fn(*mut *mut c_char) -> c_int, |
| proc_name: &'static str, |
| raw_format: bool, |
| ) -> Result<Option<Self>> { |
| let mut context: *mut c_char = ptr::null_mut(); |
| if unsafe { proc(&mut context) } == -1_i32 { |
| Err(Error::last_io_error(proc_name)) |
| } else { |
| Ok(ptr::NonNull::new(context).map(|c| Self::from_ptr(c, None, raw_format))) |
| } |
| } |
| |
| fn from_ptr(context: ptr::NonNull<c_char>, size: Option<c_uint>, raw_format: bool) -> Self { |
| #[allow(clippy::as_conversions)] |
| Self { |
| context, |
| size: size.map(|size| size as usize), |
| is_raw: raw_format, |
| context_owned: true, |
| _phantom_data: PhantomData, |
| } |
| } |
| |
| fn from_result( |
| proc_name: &'static str, |
| result: c_int, |
| context: *mut c_char, |
| raw_format: bool, |
| ) -> Result<Self> { |
| if result == -1_i32 { |
| Err(Error::last_io_error(proc_name)) |
| } else { |
| ptr::NonNull::new(context).map_or_else( |
| || Err(Error::from_io(proc_name, io::ErrorKind::InvalidData.into())), |
| |context| Ok(Self::from_ptr(context, None, raw_format)), |
| ) |
| } |
| } |
| |
| fn from_result_with_name( |
| proc_name: &'static str, |
| result: c_int, |
| context: *mut c_char, |
| name: &str, |
| raw_format: bool, |
| ) -> Result<Self> { |
| if result == -1_i32 { |
| Err(Error::last_io_error(proc_name)) |
| } else { |
| ptr::NonNull::new(context).map_or_else( |
| || { |
| Err(Error::IO1Name { |
| operation: proc_name, |
| name: name.into(), |
| source: io::ErrorKind::InvalidData.into(), |
| }) |
| }, |
| |context| Ok(Self::from_ptr(context, None, raw_format)), |
| ) |
| } |
| } |
| |
| fn from_result_with_pid( |
| proc_name: &'static str, |
| result: c_int, |
| context: *mut c_char, |
| process_id: pid_t, |
| raw_format: bool, |
| ) -> Result<Self> { |
| if result == -1_i32 { |
| let err = io::Error::last_os_error(); |
| Err(Error::from_io_pid(proc_name, process_id, err)) |
| } else { |
| ptr::NonNull::new(context).map_or_else( |
| || { |
| let err = io::ErrorKind::InvalidData.into(); |
| Err(Error::from_io_pid(proc_name, process_id, err)) |
| }, |
| |context| Ok(Self::from_ptr(context, None, raw_format)), |
| ) |
| } |
| } |
| |
| fn parse_context_color(bytes: &[u8]) -> Result<SecurityContextColors> { |
| #[allow(clippy::as_conversions)] |
| let colors: Vec<RGB> = bytes |
| .split(u8::is_ascii_whitespace) |
| .filter(|&bytes| !bytes.is_empty()) |
| .take(8) |
| .flat_map(|bytes| bytes.strip_prefix(b"#")) |
| .filter(|&bytes| !bytes.is_empty()) |
| .flat_map(|bytes| str::from_utf8(bytes).ok()) |
| .flat_map(|s| u32::from_str_radix(s, 16).ok()) |
| .filter(|&n| n <= 0x00ff_ffff_u32) |
| .map(|n| RGB { |
| red: (n & 0xff_u32) as u8, |
| green: ((n >> 8) & 0xff_u32) as u8, |
| blue: ((n >> 16) & 0xff_u32) as u8, |
| }) |
| .collect(); |
| |
| if let Ok(colors) = <[RGB; 8]>::try_from(colors) { |
| Ok(SecurityContextColors { |
| user: LayerColors { |
| background: colors[1], |
| foreground: colors[0], |
| }, |
| role: LayerColors { |
| background: colors[3], |
| foreground: colors[2], |
| }, |
| the_type: LayerColors { |
| background: colors[5], |
| foreground: colors[4], |
| }, |
| range: LayerColors { |
| background: colors[7], |
| foreground: colors[6], |
| }, |
| }) |
| } else { |
| Err(Error::from_io_name( |
| "selinux_raw_context_to_color()", |
| String::from_utf8_lossy(bytes), |
| io::ErrorKind::InvalidData.into(), |
| )) |
| } |
| } |
| } |
| |
| impl Drop for SecurityContext<'_> { |
| /// See: `freecon()`. |
| #[doc(alias = "freecon")] |
| fn drop(&mut self) { |
| let context = self.context.as_ptr(); |
| self.context = ptr::NonNull::dangling(); |
| if self.context_owned { |
| unsafe { selinux_sys::freecon(context) } |
| } |
| } |
| } |
| |
| /// List of security contexts. |
| #[derive(Debug)] |
| pub struct SecurityContextList { |
| context_list: ptr::NonNull<*mut c_char>, |
| count: usize, |
| _phantom_data: PhantomData<c_char>, |
| } |
| |
| impl SecurityContextList { |
| /// Return the managed raw pointer to [`c_char`]. |
| #[must_use] |
| pub fn as_ptr(&self) -> *const *const c_char { |
| self.context_list.as_ptr().cast() |
| } |
| |
| /// Return the managed raw pointer to [`c_char`]. |
| #[must_use] |
| pub fn as_mut_ptr(&mut self) -> *mut *mut c_char { |
| self.context_list.as_ptr() |
| } |
| |
| /// Number of security contexts present in this list. |
| #[must_use] |
| pub fn len(&self) -> usize { |
| self.count |
| } |
| |
| /// Return `true` if this list is empty. |
| #[must_use] |
| pub fn is_empty(&self) -> bool { |
| self.count == 0 |
| } |
| |
| /// Obtain a list of contexts, for the specified SELinux user identity, |
| /// that are reachable from the specified `reachable_from_context`. |
| /// |
| /// See: `get_ordered_context_list()`, `get_ordered_context_list_with_level()`. |
| #[doc(alias = "get_ordered_context_list")] |
| #[doc(alias = "get_ordered_context_list_with_level")] |
| pub fn of_se_user( |
| se_user: &str, |
| level: Option<&str>, |
| reachable_from_context: Option<&SecurityContext>, |
| ) -> Result<Self> { |
| let c_se_user = str_to_c_string(se_user)?; |
| |
| let c_level = if let Some(level) = level { |
| str_to_c_string(level).map(Some)? |
| } else { |
| None |
| }; |
| |
| let reachable_from_context = |
| reachable_from_context.map_or(ptr::null_mut(), |c| c.context.as_ptr()); |
| |
| let mut context_list: *mut *mut c_char = ptr::null_mut(); |
| |
| let (r, proc_name) = unsafe { |
| if let Some(c_level) = c_level { |
| let r = selinux_sys::get_ordered_context_list_with_level( |
| c_se_user.as_ptr(), |
| c_level.as_ptr(), |
| reachable_from_context, |
| &mut context_list, |
| ); |
| (r, "get_ordered_context_list_with_level()") |
| } else { |
| let r = selinux_sys::get_ordered_context_list( |
| c_se_user.as_ptr(), |
| reachable_from_context, |
| &mut context_list, |
| ); |
| (r, "get_ordered_context_list()") |
| } |
| }; |
| |
| if r == -1_i32 { |
| Err(Error::last_io_error(proc_name)) |
| } else { |
| ptr::NonNull::new(context_list).map_or_else( |
| || Err(Error::from_io(proc_name, io::ErrorKind::InvalidData.into())), |
| |context_list| { |
| Ok(Self { |
| context_list, |
| #[allow(clippy::cast_sign_loss, clippy::as_conversions)] |
| count: r as c_uint as usize, |
| _phantom_data: PhantomData, |
| }) |
| }, |
| ) |
| } |
| } |
| |
| /// Return the security context at the given index, if the index is valid. |
| /// |
| /// ⚠️ The returned instance does **NOT** own the context. |
| /// When the returned instance get dropped, it will **NOT** deallocate the |
| /// provided context. |
| /// Deallocation of the context will only happen when the whole list gets |
| /// dropped. |
| #[must_use] |
| pub fn get(&'_ self, index: usize, raw_format: bool) -> Option<SecurityContext<'_>> { |
| if index < self.count { |
| let context = unsafe { *self.context_list.as_ptr().wrapping_add(index) }; |
| ptr::NonNull::new(context).map(|context| SecurityContext { |
| context, |
| size: None, |
| is_raw: raw_format, |
| context_owned: false, |
| _phantom_data: PhantomData, |
| }) |
| } else { |
| None |
| } |
| } |
| |
| /// Ask the user via `stdin`/`stdout` as to which context they want from |
| /// this list of contexts, and return a new context as selected by the user. |
| /// |
| /// See: `query_user_context()`. |
| #[doc(alias = "query_user_context")] |
| pub fn user_selected_context(&self, raw_format: bool) -> Result<SecurityContext> { |
| let mut context: *mut c_char = ptr::null_mut(); |
| let r = |
| unsafe { selinux_sys::query_user_context(self.context_list.as_ptr(), &mut context) }; |
| SecurityContext::from_result("query_user_context()", r, context, raw_format) |
| } |
| } |
| |
| impl Drop for SecurityContextList { |
| /// See: `freeconary()`. |
| #[doc(alias = "freeconary")] |
| fn drop(&mut self) { |
| let context_list = self.context_list.as_ptr(); |
| self.context_list = ptr::NonNull::dangling(); |
| unsafe { selinux_sys::freeconary(context_list) } |
| } |
| } |
| |
| /// File access mode. |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub struct FileAccessMode(selinux_sys::mode_t); |
| |
| impl FileAccessMode { |
| /// Create a new file access mode, if given a non-zero `mode`. |
| #[must_use] |
| pub fn new(mode: selinux_sys::mode_t) -> Option<Self> { |
| if mode == 0 { |
| None |
| } else { |
| Some(Self(mode)) |
| } |
| } |
| |
| /// Return the mode value. |
| #[must_use] |
| pub fn mode(self) -> selinux_sys::mode_t { |
| self.0 |
| } |
| } |
| |
| /// SELinux security class. |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub struct SecurityClass(selinux_sys::security_class_t); |
| |
| impl fmt::Display for SecurityClass { |
| /// See: `security_class_to_string()`. |
| #[doc(alias = "security_class_to_string")] |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let name_ptr = unsafe { selinux_sys::security_class_to_string(self.0) }; |
| if name_ptr.is_null() { |
| write!(f, "<invalid-class>") |
| } else { |
| let c_name = unsafe { CStr::from_ptr(name_ptr) }; |
| write!(f, "{}", c_name.to_string_lossy()) |
| } |
| } |
| } |
| |
| impl SecurityClass { |
| /// Return the security class value. |
| #[must_use] |
| pub fn value(&self) -> selinux_sys::security_class_t { |
| self.0 |
| } |
| |
| /// Create a new security class, if given a non-zero `class`. |
| pub fn new(class: selinux_sys::security_class_t) -> Result<Self> { |
| if class == 0 { |
| let err = io::ErrorKind::NotFound.into(); |
| Err(Error::from_io("SecurityClass::new()", err)) |
| } else { |
| Ok(Self(class)) |
| } |
| } |
| |
| /// Return the security class corresponding to the string name, |
| /// if such class exists. |
| /// |
| /// See: `string_to_security_class()`. |
| #[doc(alias = "string_to_security_class")] |
| pub fn from_name(name: &str) -> Result<Self> { |
| let c_name = str_to_c_string(name)?; |
| let r = unsafe { selinux_sys::string_to_security_class(c_name.as_ptr()) }; |
| if r == 0 { |
| let err = io::ErrorKind::NotFound.into(); |
| Err(Error::from_io("string_to_security_class()", err)) |
| } else { |
| Ok(Self(r)) |
| } |
| } |
| |
| /// Return the name of the `access_vector` of this security class. |
| /// |
| /// See: `security_av_perm_to_string()`. |
| /// |
| /// # Safety |
| /// |
| /// The returned string must not be **modified** or **freed**. |
| #[doc(alias = "security_av_perm_to_string")] |
| pub unsafe fn access_vector_bit_name( |
| &self, |
| access_vector: selinux_sys::access_vector_t, |
| ) -> Result<&'static CStr> { |
| let name_ptr = unsafe { selinux_sys::security_av_perm_to_string(self.0, access_vector) }; |
| if name_ptr.is_null() { |
| let err = io::ErrorKind::NotFound.into(); |
| Err(Error::from_io("security_av_perm_to_string()", err)) |
| } else { |
| unsafe { Ok(CStr::from_ptr(name_ptr)) } |
| } |
| } |
| |
| /// Return the access vector bit corresponding to the given name and this |
| /// security class. |
| /// |
| /// See: `string_to_av_perm()`. |
| #[doc(alias = "string_to_av_perm")] |
| pub fn access_vector_bit(&self, name: &str) -> Result<selinux_sys::access_vector_t> { |
| let c_name = str_to_c_string(name)?; |
| let r = unsafe { selinux_sys::string_to_av_perm(self.0, c_name.as_ptr()) }; |
| if r == 0 { |
| let err = io::ErrorKind::NotFound.into(); |
| Err(Error::from_io("string_to_av_perm()", err)) |
| } else { |
| Ok(r) |
| } |
| } |
| |
| /// Compute a full access vector string representation using this security |
| /// class and `access_vector`, which may have multiple bits set. |
| /// |
| /// See: `security_av_string()`. |
| #[doc(alias = "security_av_string")] |
| pub fn full_access_vector_name( |
| &self, |
| access_vector: selinux_sys::access_vector_t, |
| ) -> Result<CAllocatedBlock<c_char>> { |
| let mut name_ptr: *mut c_char = ptr::null_mut(); |
| let r: c_int = |
| unsafe { selinux_sys::security_av_string(self.0, access_vector, &mut name_ptr) }; |
| if r == -1_i32 { |
| Err(Error::last_io_error("security_av_string()")) |
| } else { |
| CAllocatedBlock::new(name_ptr).ok_or_else(|| { |
| Error::from_io("security_av_string()", io::ErrorKind::NotFound.into()) |
| }) |
| } |
| } |
| } |
| |
| impl TryFrom<FileAccessMode> for SecurityClass { |
| type Error = Error; |
| |
| /// See: `mode_to_security_class()`. |
| #[doc(alias = "mode_to_security_class")] |
| fn try_from(mode: FileAccessMode) -> Result<Self> { |
| let r = unsafe { selinux_sys::mode_to_security_class(mode.mode()) }; |
| if r == 0 { |
| let err = io::ErrorKind::NotFound.into(); |
| Err(Error::from_io("mode_to_security_class()", err)) |
| } else { |
| Ok(Self(r)) |
| } |
| } |
| } |
| |
| /// Opaque security context. |
| #[derive(Debug)] |
| pub struct OpaqueSecurityContext { |
| context: ptr::NonNull<selinux_sys::context_s_t>, |
| _phantom_data: PhantomData<selinux_sys::context_s_t>, |
| } |
| |
| impl OpaqueSecurityContext { |
| /// Return the managed raw pointer to [`selinux_sys::context_s_t`]. |
| #[must_use] |
| pub fn as_ptr(&self) -> *const selinux_sys::context_s_t { |
| self.context.as_ptr() |
| } |
| |
| /// Return the managed raw pointer to [`selinux_sys::context_s_t`]. |
| #[must_use] |
| pub fn as_mut_ptr(&mut self) -> *mut selinux_sys::context_s_t { |
| self.context.as_ptr() |
| } |
| |
| /// Return a new context initialized to a context string. |
| /// |
| /// See: `context_new()`. |
| #[doc(alias = "context_new")] |
| pub fn new(context: &str) -> Result<Self> { |
| let c_context = str_to_c_string(context)?; |
| Self::from_c_str(&c_context) |
| } |
| |
| /// Return a new context initialized to a context string. |
| /// |
| /// See: `context_new()`. |
| #[doc(alias = "context_new")] |
| pub fn from_c_str(context: &CStr) -> Result<Self> { |
| let context = unsafe { selinux_sys::context_new(context.as_ptr()) }; |
| ptr::NonNull::new(context).map_or_else( |
| || Err(Error::last_io_error("context_new()")), |
| |context| { |
| Ok(Self { |
| context, |
| _phantom_data: PhantomData, |
| }) |
| }, |
| ) |
| } |
| |
| /// Return the string value of this security context. |
| /// |
| /// See: `context_str()`. |
| #[doc(alias = "context_str")] |
| pub fn to_c_string(&self) -> Result<CString> { |
| let r = unsafe { selinux_sys::context_str(self.context.as_ptr()) }; |
| if r.is_null() { |
| Err(Error::last_io_error("context_str()")) |
| } else { |
| Ok(unsafe { CStr::from_ptr(r) }.into()) |
| } |
| } |
| |
| /// Return the string value of this security context's type. |
| /// |
| /// See: `context_type_get()`. |
| #[doc(alias = "context_type_get")] |
| pub fn the_type(&self) -> Result<CString> { |
| self.get(selinux_sys::context_type_get, "context_type_get()") |
| } |
| |
| /// Set the type of this security context. |
| /// |
| /// See: `context_type_set()`. |
| #[doc(alias = "context_type_set")] |
| pub fn set_type_str(&self, new_value: &str) -> Result<()> { |
| let c_new_value = str_to_c_string(new_value)?; |
| self.set( |
| selinux_sys::context_type_set, |
| "context_type_set()", |
| c_new_value.as_ref(), |
| ) |
| } |
| |
| /// Set the type of this security context. |
| /// |
| /// See: `context_type_set()`. |
| #[doc(alias = "context_type_set")] |
| pub fn set_type(&self, new_value: &CStr) -> Result<()> { |
| let proc_name = "context_type_set()"; |
| self.set(selinux_sys::context_type_set, proc_name, new_value) |
| } |
| |
| /// Return the string value of this security context's range. |
| /// |
| /// See: `context_range_get()`. |
| #[doc(alias = "context_range_get")] |
| pub fn range(&self) -> Result<CString> { |
| self.get(selinux_sys::context_range_get, "context_range_get()") |
| } |
| |
| /// Set the range of this security context. |
| /// |
| /// See: `context_range_set()`. |
| #[doc(alias = "context_range_set")] |
| pub fn set_range_str(&self, new_value: &str) -> Result<()> { |
| let c_new_value = str_to_c_string(new_value)?; |
| self.set( |
| selinux_sys::context_range_set, |
| "context_range_set()", |
| c_new_value.as_ref(), |
| ) |
| } |
| |
| /// Set the range of this security context. |
| /// |
| /// See: `context_range_set()`. |
| #[doc(alias = "context_range_set")] |
| pub fn set_range(&self, new_value: &CStr) -> Result<()> { |
| let proc_name = "context_range_set()"; |
| self.set(selinux_sys::context_range_set, proc_name, new_value) |
| } |
| |
| /// Return the string value of this security context's role. |
| /// |
| /// See: `context_role_get()`. |
| #[doc(alias = "context_role_get")] |
| pub fn role(&self) -> Result<CString> { |
| self.get(selinux_sys::context_role_get, "context_role_get()") |
| } |
| |
| /// Set the role of this security context. |
| /// |
| /// See: `context_role_set()`. |
| #[doc(alias = "context_role_set")] |
| pub fn set_role_str(&self, new_value: &str) -> Result<()> { |
| let c_new_value = str_to_c_string(new_value)?; |
| self.set( |
| selinux_sys::context_role_set, |
| "context_role_set()", |
| c_new_value.as_ref(), |
| ) |
| } |
| |
| /// Set the role of this security context. |
| /// |
| /// See: `context_role_set()`. |
| #[doc(alias = "context_role_set")] |
| pub fn set_role(&self, new_value: &CStr) -> Result<()> { |
| let proc_name = "context_role_set()"; |
| self.set(selinux_sys::context_role_set, proc_name, new_value) |
| } |
| |
| /// Return the string value of this security context's user. |
| /// |
| /// See: `context_user_get()`. |
| #[doc(alias = "context_user_get")] |
| pub fn user(&self) -> Result<CString> { |
| self.get(selinux_sys::context_user_get, "context_user_get()") |
| } |
| |
| /// Set the user of this security context. |
| /// |
| /// See: `context_user_set()`. |
| #[doc(alias = "context_user_set")] |
| pub fn set_user_str(&self, new_value: &str) -> Result<()> { |
| let c_new_value = str_to_c_string(new_value)?; |
| self.set( |
| selinux_sys::context_user_set, |
| "context_user_set()", |
| c_new_value.as_ref(), |
| ) |
| } |
| |
| /// Set the user of this security context. |
| /// |
| /// See: `context_user_set()`. |
| #[doc(alias = "context_user_set")] |
| pub fn set_user(&self, new_value: &CStr) -> Result<()> { |
| let proc_name = "context_user_set()"; |
| self.set(selinux_sys::context_user_set, proc_name, new_value) |
| } |
| |
| fn get( |
| &self, |
| proc: unsafe extern "C" fn(selinux_sys::context_t) -> *const c_char, |
| proc_name: &'static str, |
| ) -> Result<CString> { |
| let r = unsafe { proc(self.context.as_ptr()) }; |
| if r.is_null() { |
| Err(Error::last_io_error(proc_name)) |
| } else { |
| Ok(unsafe { CStr::from_ptr(r) }.into()) |
| } |
| } |
| |
| fn set( |
| &self, |
| proc: unsafe extern "C" fn(selinux_sys::context_t, *const c_char) -> c_int, |
| proc_name: &'static str, |
| new_value: &CStr, |
| ) -> Result<()> { |
| if unsafe { proc(self.context.as_ptr(), new_value.as_ptr()) } == 0_i32 { |
| Ok(()) |
| } else { |
| Err(Error::last_io_error(proc_name)) |
| } |
| } |
| } |
| |
| impl Drop for OpaqueSecurityContext { |
| /// See: `context_free()`. |
| #[doc(alias = "context_free")] |
| fn drop(&mut self) { |
| let context = self.context.as_ptr(); |
| self.context = ptr::NonNull::dangling(); |
| unsafe { selinux_sys::context_free(context) }; |
| } |
| } |
| |
| impl fmt::Display for OpaqueSecurityContext { |
| /// See: `context_str()`. |
| #[doc(alias = "context_str")] |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let ptr = unsafe { selinux_sys::context_str(self.context.as_ptr()) }; |
| let context = if ptr.is_null() { |
| Cow::Borrowed("<null>") |
| } else { |
| unsafe { CStr::from_ptr(ptr) }.to_string_lossy() |
| }; |
| write!(f, "{context}") |
| } |
| } |
| |
| /// Support of SELinux in the running kernel. |
| #[non_exhaustive] |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub enum KernelSupport { |
| /// SELinux is unsupported. |
| Unsupported, |
| /// SELinux is supported. |
| SELinux, |
| /// SELinux is supported, with Multi Level Security. |
| SELinuxMLS, |
| } |
| |
| /// SELinux enforcing mode. |
| #[non_exhaustive] |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub enum SELinuxMode { |
| /// SELinux is not enforcing. |
| NotRunning, |
| /// SELinux is permissive. |
| Permissive, |
| /// SELinux is enforcing. |
| Enforcing, |
| } |
| |
| /// SELinux handling of undefined object classes and permissions. |
| #[non_exhaustive] |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub enum UndefinedHandling { |
| /// Undefined object classes and permissions are allowed. |
| Allowed, |
| /// Undefined object classes and permissions are deined at run time. |
| DeniedAtRunTime, |
| /// Undefined object classes and permissions are rejected at policy load time. |
| RejectedAtLoadTime, |
| } |
| |
| /// Protection checked by SELinux on `mmap()` and `mprotect()` calls. |
| #[non_exhaustive] |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub enum ProtectionCheckingMode { |
| /// Actual protection that will be applied by the kernel |
| /// (including the effects of `READ_IMPLIES_EXEC`). |
| CheckingActualProtection, |
| /// Protection requested by the application. |
| CheckingRequestedProtection, |
| } |
| |
| /// Determine the support of SELinux in the running kernel. |
| /// |
| /// See: `is_selinux_enabled()`, `is_selinux_mls_enabled()`. |
| #[doc(alias = "is_selinux_enabled")] |
| #[doc(alias = "is_selinux_mls_enabled")] |
| #[must_use] |
| pub fn kernel_support() -> KernelSupport { |
| if unsafe { selinux_sys::is_selinux_mls_enabled() } != 0_i32 { |
| KernelSupport::SELinuxMLS |
| } else if unsafe { selinux_sys::is_selinux_enabled() } != 0_i32 { |
| KernelSupport::SELinux |
| } else { |
| KernelSupport::Unsupported |
| } |
| } |
| |
| /// Determine how the system was set up to run SELinux. |
| /// |
| /// See: `selinux_getenforcemode()`. |
| #[doc(alias = "selinux_getenforcemode")] |
| pub fn boot_mode() -> Result<SELinuxMode> { |
| let mut enforce: c_int = -1; |
| if unsafe { selinux_sys::selinux_getenforcemode(&mut enforce) } == -1_i32 { |
| Err(Error::last_io_error("selinux_getenforcemode()")) |
| } else { |
| match enforce { |
| -1_i32 => Ok(SELinuxMode::NotRunning), |
| 0_i32 => Ok(SELinuxMode::Permissive), |
| _ => Ok(SELinuxMode::Enforcing), |
| } |
| } |
| } |
| |
| /// Determine the current SELinux enforcing mode. |
| /// |
| /// See: `security_getenforce()`. |
| #[doc(alias = "security_getenforce")] |
| #[must_use] |
| pub fn current_mode() -> SELinuxMode { |
| match unsafe { selinux_sys::security_getenforce() } { |
| -1_i32 => SELinuxMode::NotRunning, |
| 0_i32 => SELinuxMode::Permissive, |
| _ => SELinuxMode::Enforcing, |
| } |
| } |
| |
| /// Set the current SELinux enforcing mode. |
| /// |
| /// See: `security_disable()`, `security_setenforce()`. |
| #[doc(alias = "security_disable")] |
| #[doc(alias = "security_setenforce")] |
| pub fn set_current_mode(new_mode: SELinuxMode) -> Result<()> { |
| let (r, proc_name) = match new_mode { |
| SELinuxMode::NotRunning => { |
| let r = unsafe { selinux_sys::security_disable() }; |
| (r, "security_disable()") |
| } |
| |
| SELinuxMode::Permissive => { |
| let r = unsafe { selinux_sys::security_setenforce(0) }; |
| (r, "security_setenforce()") |
| } |
| |
| SELinuxMode::Enforcing => { |
| let r = unsafe { selinux_sys::security_setenforce(1) }; |
| (r, "security_setenforce()") |
| } |
| }; |
| |
| ret_val_to_result(proc_name, r) |
| } |
| |
| /// Return the current SELinux handling of undefined object classes |
| /// and permissions. |
| /// |
| /// See: `security_reject_unknown()`, `security_deny_unknown()`. |
| #[doc(alias = "security_reject_unknown")] |
| #[doc(alias = "security_deny_unknown")] |
| pub fn undefined_handling() -> Result<UndefinedHandling> { |
| let mut reject_unknown: c_int = |
| unsafe { (OptionalNativeFunctions::get().security_reject_unknown)() }; |
| if reject_unknown == -1_i32 { |
| let err = io::Error::last_os_error(); |
| if err.raw_os_error() == Some(libc::ENOSYS) { |
| reject_unknown = 0_i32; |
| } else { |
| return Err(Error::from_io("security_reject_unknown()", err)); |
| } |
| } |
| |
| if reject_unknown == 0_i32 { |
| match unsafe { selinux_sys::security_deny_unknown() } { |
| -1_i32 => Err(Error::last_io_error("security_deny_unknown()")), |
| |
| 0_i32 => Ok(UndefinedHandling::Allowed), |
| |
| _ => Ok(UndefinedHandling::DeniedAtRunTime), |
| } |
| } else { |
| Ok(UndefinedHandling::RejectedAtLoadTime) |
| } |
| } |
| |
| /// Determine the protection currently checked by SELinux on `mmap()` and |
| /// `mprotect()` calls. |
| /// |
| /// See: `security_get_checkreqprot()`. |
| #[doc(alias = "security_get_checkreqprot")] |
| pub fn protection_checking_mode() -> Result<ProtectionCheckingMode> { |
| match unsafe { selinux_sys::security_get_checkreqprot() } { |
| -1_i32 => Err(Error::last_io_error("security_get_checkreqprot()")), |
| |
| 0_i32 => Ok(ProtectionCheckingMode::CheckingActualProtection), |
| |
| _ => Ok(ProtectionCheckingMode::CheckingRequestedProtection), |
| } |
| } |
| |
| fn dynamic_mapping_into_native_form<'mapping, 'key, 'value, K, V, O>( |
| mapping: &'mapping [(K, V)], |
| c_string_storage: &mut HashMap<&'key str, CString>, |
| ) -> Result<Vec<selinux_sys::security_class_mapping>> |
| where |
| 'mapping: 'key, |
| 'value: 'key, |
| K: AsRef<str> + 'key, |
| V: AsRef<[O]>, |
| O: AsRef<str> + 'value, |
| { |
| let mut c_map = Vec::with_capacity(mapping.len() + 1); |
| |
| for (name, permissions) in mapping { |
| let mut element: selinux_sys::security_class_mapping = unsafe { mem::zeroed() }; |
| |
| if permissions.as_ref().len() >= element.perms.len() { |
| let err = io::ErrorKind::InvalidInput.into(); |
| return Err(Error::from_io("SELinux::set_dynamic_mapping()", err)); |
| } |
| |
| element.name = match c_string_storage.entry(name.as_ref()) { |
| hash_map::Entry::Vacant(entry) => { |
| let c_name = str_to_c_string(name.as_ref())?; |
| entry.insert(c_name).as_ptr() |
| } |
| |
| hash_map::Entry::Occupied(entry) => entry.get().as_ptr(), |
| }; |
| |
| for (index, permission) in permissions.as_ref().iter().enumerate() { |
| element.perms[index] = match c_string_storage.entry(permission.as_ref()) { |
| hash_map::Entry::Vacant(entry) => { |
| let c_permission = str_to_c_string(permission.as_ref())?; |
| entry.insert(c_permission).as_ptr() |
| } |
| |
| hash_map::Entry::Occupied(entry) => entry.get().as_ptr(), |
| }; |
| } |
| |
| c_map.push(element); |
| } |
| c_map.push(unsafe { mem::zeroed() }); // End of the array. |
| Ok(c_map) |
| } |
| |
| /// Establishes a mapping from a user-provided ordering of object classes |
| /// and permissions to the numbers actually used by the loaded system policy. |
| /// |
| /// See: `selinux_set_mapping()`. |
| #[doc(alias = "selinux_set_mapping")] |
| pub fn set_dynamic_mapping<K, V, O>(mapping: &[(K, V)]) -> Result<()> |
| where |
| K: AsRef<str>, |
| V: AsRef<[O]>, |
| O: AsRef<str>, |
| { |
| // The `selinux_set_mapping()` parameter holds pointers to null-terminated strings. |
| // |
| // We transform the input `mapping` into an array of `security_class_mapping` |
| // by transforming `&str` instances into CString instances and storing |
| // them into `c_string_storage`. Pointers to these `CString`s are then stored |
| // into the `security_class_mapping` structures. |
| let mut c_string_storage = HashMap::<&str, CString>::with_capacity(mapping.len() * 3); |
| let mut c_map = dynamic_mapping_into_native_form(mapping, &mut c_string_storage)?; |
| |
| let r = unsafe { selinux_sys::selinux_set_mapping(c_map.as_mut_ptr()) }; |
| ret_val_to_result("selinux_set_mapping()", r) |
| } |
| |
| /// Flush the SELinux class cache, *e.g.*, upon a policy reload. |
| /// |
| /// This function requires `libselinux` version `3.1` or later. |
| /// |
| /// See: `selinux_flush_class_cache()`. |
| #[doc(alias = "selinux_flush_class_cache")] |
| pub fn flush_class_cache() -> Result<()> { |
| Error::clear_errno(); |
| unsafe { (OptionalNativeFunctions::get().selinux_flush_class_cache)() } |
| |
| let err = io::Error::last_os_error(); |
| match err.raw_os_error() { |
| None | Some(0_i32) => Ok(()), |
| _ => Err(Error::from_io("selinux_flush_class_cache()", err)), |
| } |
| } |
| |
| /// Get the SELinux user name and level for a given Linux user name. |
| /// |
| /// See: `getseuser()`, `getseuserbyname()`. |
| #[doc(alias = "getseuser")] |
| #[doc(alias = "getseuserbyname")] |
| pub fn se_user_and_level( |
| user_name: &str, |
| service: Option<&str>, |
| ) -> Result<(CAllocatedBlock<c_char>, CAllocatedBlock<c_char>)> { |
| let c_user_name = str_to_c_string(user_name)?; |
| let c_service = if let Some(service) = service { |
| Some(str_to_c_string(service)?) |
| } else { |
| None |
| }; |
| |
| let mut se_user_ptr: *mut c_char = ptr::null_mut(); |
| let mut level_ptr: *mut c_char = ptr::null_mut(); |
| |
| let (r, proc_name) = if let Some(c_service) = c_service { |
| let r: c_int = unsafe { |
| selinux_sys::getseuser( |
| c_user_name.as_ptr(), |
| c_service.as_ptr(), |
| &mut se_user_ptr, |
| &mut level_ptr, |
| ) |
| }; |
| |
| (r, "getseuser()") |
| } else { |
| let r = unsafe { |
| selinux_sys::getseuserbyname(c_user_name.as_ptr(), &mut se_user_ptr, &mut level_ptr) |
| }; |
| |
| (r, "getseuserbyname()") |
| }; |
| |
| if r == -1_i32 { |
| Err(Error::last_io_error(proc_name)) |
| } else if se_user_ptr.is_null() || level_ptr.is_null() { |
| Err(Error::from_io(proc_name, io::ErrorKind::InvalidData.into())) |
| } else { |
| let se_user = CAllocatedBlock::new(se_user_ptr) |
| .ok_or_else(|| Error::from_io(proc_name, io::ErrorKind::InvalidInput.into()))?; |
| |
| let level = CAllocatedBlock::new(level_ptr) |
| .ok_or_else(|| Error::from_io(proc_name, io::ErrorKind::InvalidInput.into()))?; |
| |
| Ok((se_user, level)) |
| } |
| } |
| |
| /// Force a reset of the loaded configuration. |
| /// |
| /// See: `selinux_reset_config()`. |
| #[doc(alias = "selinux_reset_config")] |
| pub fn reset_config() { |
| unsafe { selinux_sys::selinux_reset_config() } |
| } |
| |
| /// Get the default type (domain) for role, and set type to refer to it. |
| /// |
| /// See: `get_default_type()`. |
| #[doc(alias = "get_default_type")] |
| pub fn default_type_for_role(role: &str) -> Result<CAllocatedBlock<c_char>> { |
| let mut c_type: *mut c_char = ptr::null_mut(); |
| let c_role = str_to_c_string(role)?; |
| if unsafe { selinux_sys::get_default_type(c_role.as_ptr(), &mut c_type) } == -1_i32 { |
| Err(Error::last_io_error("get_default_type()")) |
| } else { |
| CAllocatedBlock::new(c_type) |
| .ok_or_else(|| Error::from_io("get_default_type()", io::ErrorKind::InvalidData.into())) |
| } |
| } |