From 8298173554b15d9a8c19519a5618ca4ff489f506 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 23 Apr 2025 09:41:27 +0200 Subject: [PATCH 1/2] mkfifo: implement selinux support Co-authored-by: Daniel Hofstetter merge --- Cargo.toml | 1 + src/uu/mkfifo/Cargo.toml | 3 ++ src/uu/mkfifo/src/mkfifo.rs | 37 ++++++++++++++------ tests/by-util/test_mkfifo.rs | 65 ++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92a43dbdc..1dc98e2e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ feat_selinux = [ "id/selinux", "ls/selinux", "mkdir/selinux", + "mkfifo/selinux", "mknod/selinux", "stat/selinux", "selinux", diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index 86d9d0eed..bb87cc80e 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -22,6 +22,9 @@ clap = { workspace = true } libc = { workspace = true } uucore = { workspace = true, features = ["fs", "mode"] } +[features] +selinux = ["uucore/selinux"] + [[bin]] name = "mkfifo" path = "src/main.rs" diff --git a/src/uu/mkfifo/src/mkfifo.rs b/src/uu/mkfifo/src/mkfifo.rs index e6e904014..fe41a515e 100644 --- a/src/uu/mkfifo/src/mkfifo.rs +++ b/src/uu/mkfifo/src/mkfifo.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use clap::{Arg, ArgAction, Command}; +use clap::{Arg, ArgAction, Command, value_parser}; use libc::mkfifo; use std::ffi::CString; use std::fs; @@ -17,7 +17,7 @@ static ABOUT: &str = help_about!("mkfifo.md"); mod options { pub static MODE: &str = "mode"; - pub static SE_LINUX_SECURITY_CONTEXT: &str = "Z"; + pub static SELINUX: &str = "Z"; pub static CONTEXT: &str = "context"; pub static FIFO: &str = "fifo"; } @@ -26,13 +26,6 @@ mod options { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; - if matches.contains_id(options::CONTEXT) { - return Err(USimpleError::new(1, "--context is not implemented")); - } - if matches.get_flag(options::SE_LINUX_SECURITY_CONTEXT) { - return Err(USimpleError::new(1, "-Z is not implemented")); - } - let mode = match matches.get_one::(options::MODE) { // if mode is passed, ignore umask Some(m) => match usize::from_str_radix(m, 8) { @@ -67,6 +60,27 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { format!("cannot set permissions on {}: {e}", f.quote()), )); } + + // Apply SELinux context if requested + #[cfg(feature = "selinux")] + { + // Extract the SELinux related flags and options + let set_selinux_context = matches.get_flag(options::SELINUX); + let context = matches.get_one::(options::CONTEXT); + + if set_selinux_context || context.is_some() { + use std::path::Path; + if let Err(e) = + uucore::selinux::set_selinux_security_context(Path::new(&f), context) + { + let _ = fs::remove_file(f); + return Err(USimpleError::new( + 1, + format!("failed to set SELinux security context: {e}"), + )); + } + } + } } Ok(()) @@ -86,7 +100,7 @@ pub fn uu_app() -> Command { .value_name("MODE"), ) .arg( - Arg::new(options::SE_LINUX_SECURITY_CONTEXT) + Arg::new(options::SELINUX) .short('Z') .help("set the SELinux security context to default type") .action(ArgAction::SetTrue), @@ -95,6 +109,9 @@ pub fn uu_app() -> Command { Arg::new(options::CONTEXT) .long(options::CONTEXT) .value_name("CTX") + .value_parser(value_parser!(String)) + .num_args(0..=1) + .require_equals(true) .help( "like -Z, or if CTX is specified then set the SELinux \ or SMACK security context to CTX", diff --git a/tests/by-util/test_mkfifo.rs b/tests/by-util/test_mkfifo.rs index 79eed4d62..c9c62b41f 100644 --- a/tests/by-util/test_mkfifo.rs +++ b/tests/by-util/test_mkfifo.rs @@ -2,6 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. + +// spell-checker:ignore nconfined + use uutests::new_ucmd; use uutests::util::TestScenario; use uutests::util_name; @@ -101,3 +104,65 @@ fn test_create_fifo_with_umask() { test_fifo_creation(0o022, "prw-r--r--"); // spell-checker:disable-line test_fifo_creation(0o777, "p---------"); // spell-checker:disable-line } + +#[test] +#[cfg(feature = "feat_selinux")] +fn test_mkfifo_selinux() { + use std::process::Command; + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + let dest = "test_file"; + let args = [ + "-Z", + "--context", + "--context=unconfined_u:object_r:user_tmp_t:s0", + ]; + for arg in args { + ts.ucmd().arg(arg).arg(dest).succeeds(); + assert!(at.is_fifo("test_file")); + + let getfattr_output = Command::new("getfattr") + .arg(at.plus_as_string(dest)) + .arg("-n") + .arg("security.selinux") + .output() + .expect("Failed to run `getfattr` on the destination file"); + println!("{:?}", getfattr_output); + assert!( + getfattr_output.status.success(), + "getfattr did not run successfully: {}", + String::from_utf8_lossy(&getfattr_output.stderr) + ); + + let stdout = String::from_utf8_lossy(&getfattr_output.stdout); + assert!( + stdout.contains("unconfined_u"), + "Expected 'foo' not found in getfattr output:\n{stdout}" + ); + at.remove(&at.plus_as_string(dest)); + } +} + +#[test] +#[cfg(feature = "feat_selinux")] +fn test_mkfifo_selinux_invalid() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let dest = "orig"; + + let args = [ + "--context=a", + "--context=unconfined_u:object_r:user_tmp_t:s0:a", + "--context=nconfined_u:object_r:user_tmp_t:s0", + ]; + for arg in args { + new_ucmd!() + .arg(arg) + .arg(dest) + .fails() + .stderr_contains("Failed to"); + if at.file_exists(dest) { + at.remove(dest); + } + } +} From 4a94a4e1dc9ba7905eb13c714182bceb2ceccfdd Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 24 Apr 2025 21:53:55 +0200 Subject: [PATCH 2/2] spell: add getfattr in the ignore list --- .vscode/cspell.dictionaries/jargon.wordlist.txt | 1 + tests/by-util/test_cp.rs | 2 +- tests/by-util/test_mkdir.rs | 2 +- tests/by-util/test_mknod.rs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 8739b671b..6358f3c76 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -47,6 +47,7 @@ flamegraph fsxattr fullblock getfacl +getfattr getopt gibi gibibytes diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 72eddfd9f..2361201e6 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs -// spell-checker:ignore bdfl hlsl IRWXO IRWXG getfattr +// spell-checker:ignore bdfl hlsl IRWXO IRWXG use uutests::at_and_ucmd; use uutests::new_ucmd; use uutests::path_concat; diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index 3eaec9774..bfb65590c 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore bindgen getfattr testtest +// spell-checker:ignore bindgen testtest #![allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] diff --git a/tests/by-util/test_mknod.rs b/tests/by-util/test_mknod.rs index abeecf561..6d393c2f6 100644 --- a/tests/by-util/test_mknod.rs +++ b/tests/by-util/test_mknod.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore getfattr nconfined +// spell-checker:ignore nconfined use uutests::new_ucmd; use uutests::util::TestScenario;