1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

uucore: move the selinux function

This commit is contained in:
Sylvestre Ledru 2025-04-08 17:10:27 -04:00
parent 81c02b7408
commit 842f47b372
8 changed files with 145 additions and 56 deletions

1
Cargo.lock generated
View file

@ -3585,6 +3585,7 @@ dependencies = [
"number_prefix", "number_prefix",
"os_display", "os_display",
"regex", "regex",
"selinux",
"sha1", "sha1",
"sha2", "sha2",
"sha3", "sha3",

View file

@ -50,6 +50,7 @@ feat_selinux = [
"cp/selinux", "cp/selinux",
"id/selinux", "id/selinux",
"ls/selinux", "ls/selinux",
"mkdir/selinux",
"selinux", "selinux",
"feat_require_selinux", "feat_require_selinux",
] ]

View file

@ -19,9 +19,10 @@ path = "src/mkdir.rs"
[dependencies] [dependencies]
clap = { workspace = true } clap = { workspace = true }
selinux = { workspace = true }
uucore = { workspace = true, features = ["fs", "mode", "fsxattr"] } uucore = { workspace = true, features = ["fs", "mode", "fsxattr"] }
[features]
selinux = ["uucore/selinux"]
[[bin]] [[bin]]
name = "mkdir" name = "mkdir"

View file

@ -8,7 +8,6 @@
use clap::builder::ValueParser; use clap::builder::ValueParser;
use clap::parser::ValuesRef; use clap::parser::ValuesRef;
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use selinux::SecurityContext;
use std::ffi::OsString; use std::ffi::OsString;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[cfg(not(windows))] #[cfg(not(windows))]
@ -75,58 +74,6 @@ fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
mode::strip_minus_from_mode(args) mode::strip_minus_from_mode(args)
} }
// Add a new function to handle setting the SELinux security context
#[cfg(target_os = "linux")]
fn set_selinux_security_context(path: &Path, context: Option<&String>) -> Result<(), String> {
// Get SELinux kernel support
let support = selinux::kernel_support();
// If SELinux is not enabled, return early
if support == selinux::KernelSupport::Unsupported {
return Err("SELinux is not enabled on this system".to_string());
}
// If a specific context was provided, use it
if let Some(ctx_str) = context {
// Use the provided context
match SecurityContext::of_path(path, false, false) {
Ok(_) => {
// Create a CString from the context string
let c_context = std::ffi::CString::new(ctx_str.as_str())
.map_err(|_| "Invalid context string (contains null bytes)".to_string())?;
// Create a security context from the string
let security_context = match selinux::OpaqueSecurityContext::from_c_str(&c_context)
{
Ok(ctx) => ctx,
Err(e) => return Err(format!("Failed to create security context: {}", e)),
};
// Convert back to string for the API
let context_str = match security_context.to_c_string() {
Ok(ctx) => ctx,
Err(e) => return Err(format!("Failed to convert context to string: {}", e)),
};
// Set the context on the file
let sc = SecurityContext::from_c_str(&context_str, false);
match sc.set_for_path(path, false, false) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Failed to set context: {}", e)),
}
}
Err(e) => Err(format!("Failed to get current context: {}", e)),
}
} else {
// If no context was specified, use the default context for the path
match SecurityContext::set_default_for_path(path) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Failed to set default context: {}", e)),
}
}
}
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut args = args.collect_lossy(); let mut args = args.collect_lossy();
@ -373,7 +320,9 @@ fn create_dir(
// Apply SELinux context if requested // Apply SELinux context if requested
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
if set_selinux_context { if set_selinux_context {
if let Err(e) = set_selinux_security_context(path, context) { if let Err(e) = uucore::selinux_support::set_selinux_security_context(path, context)
{
let _ = std::fs::remove_dir(path);
return Err(USimpleError::new( return Err(USimpleError::new(
1, 1,
format!("failed to set SELinux security context: {}", e), format!("failed to set SELinux security context: {}", e),

View file

@ -58,6 +58,7 @@ crc32fast = { workspace = true, optional = true }
regex = { workspace = true, optional = true } regex = { workspace = true, optional = true }
bigdecimal = { workspace = true, optional = true } bigdecimal = { workspace = true, optional = true }
num-traits = { workspace = true, optional = true } num-traits = { workspace = true, optional = true }
selinux = { workspace = true, optional = true }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
walkdir = { workspace = true, optional = true } walkdir = { workspace = true, optional = true }
@ -105,13 +106,14 @@ format = [
mode = ["libc"] mode = ["libc"]
perms = ["entries", "libc", "walkdir"] perms = ["entries", "libc", "walkdir"]
buf-copy = [] buf-copy = []
parser = ["extendedbigdecimal", "glob", "num-traits"]
pipes = [] pipes = []
process = ["libc"] process = ["libc"]
proc-info = ["tty", "walkdir"] proc-info = ["tty", "walkdir"]
quoting-style = [] quoting-style = []
ranges = [] ranges = []
ringbuffer = [] ringbuffer = []
parser = ["extendedbigdecimal", "glob", "num-traits"] selinux = ["dep:selinux"]
signals = [] signals = []
sum = [ sum = [
"digest", "digest",

View file

@ -66,6 +66,8 @@ pub mod tty;
#[cfg(all(unix, feature = "fsxattr"))] #[cfg(all(unix, feature = "fsxattr"))]
pub mod fsxattr; pub mod fsxattr;
#[cfg(all(target_os = "linux", feature = "selinux"))]
pub mod selinux;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))] #[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
pub mod signals; pub mod signals;
#[cfg(all( #[cfg(all(

View file

@ -0,0 +1,130 @@
// This file is part of the uutils uucore package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::path::Path;
use selinux::SecurityContext;
/// Sets the SELinux security context for the given filesystem path.
///
/// If a specific context is provided, it attempts to set this context explicitly.
/// Otherwise, it applies the default SELinux context for the provided path.
///
/// # Arguments
///
/// * `path` - Filesystem path on which to set the SELinux context.
/// * `context` - Optional SELinux context string to explicitly set.
///
/// # Errors
///
/// Returns an error if:
/// - SELinux is not enabled on the system.
/// - The provided context is invalid or cannot be applied.
/// - The default SELinux context cannot be set.
///
/// # Examples
///
/// Setting default context:
/// ```
/// use std::path::Path;
/// use uucore::selinux::set_selinux_security_context;
///
/// // Set the default SELinux context for a file
/// let result = set_selinux_security_context(Path::new("/path/to/file"), None);
/// if let Err(err) = result {
/// eprintln!("Failed to set default context: {}", err);
/// }
/// ```
///
/// Setting specific context:
/// ```
/// use std::path::Path;
/// use uucore::selinux::set_selinux_security_context;
///
/// // Set a specific SELinux context for a file
/// let context = String::from("unconfined_u:object_r:user_home_t:s0");
/// let result = set_selinux_security_context(Path::new("/path/to/file"), Some(&context));
/// if let Err(err) = result {
/// eprintln!("Failed to set context: {}", err);
/// }
/// ```
pub fn set_selinux_security_context(path: &Path, context: Option<&String>) -> Result<(), String> {
// Check if SELinux is enabled on the system
if selinux::kernel_support() == selinux::KernelSupport::Unsupported {
return Err("SELinux is not enabled on this system".into());
}
if let Some(ctx_str) = context {
// Create a CString from the provided context string
let c_context = std::ffi::CString::new(ctx_str.as_str())
.map_err(|_| "Invalid context string (contains null bytes)".to_string())?;
// Convert the CString into an SELinux security context
let security_context = selinux::OpaqueSecurityContext::from_c_str(&c_context)
.map_err(|e| format!("Failed to create security context: {}", e))?;
// Set the provided security context on the specified path
SecurityContext::from_c_str(
&security_context.to_c_string().map_err(|e| e.to_string())?,
false,
)
.set_for_path(path, false, false)
.map_err(|e| format!("Failed to set context: {}", e))
} else {
// If no context provided, set the default SELinux context for the path
SecurityContext::set_default_for_path(path)
.map_err(|e| format!("Failed to set default context: {}", e))
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
#[test]
fn test_selinux_context_setting() {
let tmpfile = NamedTempFile::new().expect("Failed to create tempfile");
let path = tmpfile.path();
let result = set_selinux_security_context(path, None);
if result.is_ok() {
// SELinux enabled and successfully set default context
assert!(true, "Successfully set SELinux context");
} else {
let err = result.unwrap_err();
let valid_errors = [
"SELinux is not enabled on this system",
&format!(
"Failed to set default context: selinux_lsetfilecon_default() failed on path '{}'",
path.display()
),
];
assert!(
valid_errors.contains(&err.as_str()),
"Unexpected error message: {}",
err
);
}
}
#[test]
fn test_invalid_context_string_error() {
let tmpfile = NamedTempFile::new().expect("Failed to create tempfile");
let path = tmpfile.path();
// Pass a context string containing a null byte to trigger CString::new error
let invalid_context = String::from("invalid\0context");
let result = set_selinux_security_context(path, Some(&invalid_context));
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"Invalid context string (contains null bytes)"
);
}
}

View file

@ -103,6 +103,9 @@ pub use crate::features::fsext;
#[cfg(all(unix, feature = "fsxattr"))] #[cfg(all(unix, feature = "fsxattr"))]
pub use crate::features::fsxattr; pub use crate::features::fsxattr;
#[cfg(all(target_os = "linux", feature = "selinux"))]
pub use crate::features::selinux;
//## core functions //## core functions
#[cfg(unix)] #[cfg(unix)]