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:
parent
81c02b7408
commit
842f47b372
8 changed files with 145 additions and 56 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3585,6 +3585,7 @@ dependencies = [
|
||||||
"number_prefix",
|
"number_prefix",
|
||||||
"os_display",
|
"os_display",
|
||||||
"regex",
|
"regex",
|
||||||
|
"selinux",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sha3",
|
"sha3",
|
||||||
|
|
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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(
|
||||||
|
|
130
src/uucore/src/lib/features/selinux.rs
Normal file
130
src/uucore/src/lib/features/selinux.rs
Normal 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)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue