Skip to content

Commit 6560cba

Browse files
committed
uucore: move the selinux function
1 parent 8e9832c commit 6560cba

File tree

8 files changed

+118
-56
lines changed

8 files changed

+118
-56
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ feat_selinux = [
5252
"cp/selinux",
5353
"id/selinux",
5454
"ls/selinux",
55+
"mkdir/selinux",
5556
"selinux",
5657
"feat_require_selinux",
5758
]

src/uu/mkdir/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ path = "src/mkdir.rs"
2121

2222
[dependencies]
2323
clap = { workspace = true }
24-
selinux = { workspace = true }
2524
uucore = { workspace = true, features = ["fs", "mode", "fsxattr"] }
2625

26+
[features]
27+
selinux = ["uucore/selinux"]
2728

2829
[[bin]]
2930
name = "mkdir"

src/uu/mkdir/src/mkdir.rs

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use clap::builder::ValueParser;
99
use clap::parser::ValuesRef;
1010
use clap::{Arg, ArgAction, ArgMatches, Command};
11-
use selinux::SecurityContext;
1211
use std::ffi::OsString;
1312
use std::path::{Path, PathBuf};
1413
#[cfg(not(windows))]
@@ -75,58 +74,6 @@ fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
7574
mode::strip_minus_from_mode(args)
7675
}
7776

78-
// Add a new function to handle setting the SELinux security context
79-
#[cfg(target_os = "linux")]
80-
fn set_selinux_security_context(path: &Path, context: Option<&String>) -> Result<(), String> {
81-
// Get SELinux kernel support
82-
let support = selinux::kernel_support();
83-
84-
// If SELinux is not enabled, return early
85-
if support == selinux::KernelSupport::Unsupported {
86-
return Err("SELinux is not enabled on this system".to_string());
87-
}
88-
89-
// If a specific context was provided, use it
90-
if let Some(ctx_str) = context {
91-
// Use the provided context
92-
match SecurityContext::of_path(path, false, false) {
93-
Ok(_) => {
94-
// Create a CString from the context string
95-
let c_context = std::ffi::CString::new(ctx_str.as_str())
96-
.map_err(|_| "Invalid context string (contains null bytes)".to_string())?;
97-
98-
// Create a security context from the string
99-
let security_context = match selinux::OpaqueSecurityContext::from_c_str(&c_context)
100-
{
101-
Ok(ctx) => ctx,
102-
Err(e) => return Err(format!("Failed to create security context: {}", e)),
103-
};
104-
105-
// Convert back to string for the API
106-
let context_str = match security_context.to_c_string() {
107-
Ok(ctx) => ctx,
108-
Err(e) => return Err(format!("Failed to convert context to string: {}", e)),
109-
};
110-
111-
// Set the context on the file
112-
let sc = SecurityContext::from_c_str(&context_str, false);
113-
114-
match sc.set_for_path(path, false, false) {
115-
Ok(_) => Ok(()),
116-
Err(e) => Err(format!("Failed to set context: {}", e)),
117-
}
118-
}
119-
Err(e) => Err(format!("Failed to get current context: {}", e)),
120-
}
121-
} else {
122-
// If no context was specified, use the default context for the path
123-
match SecurityContext::set_default_for_path(path) {
124-
Ok(_) => Ok(()),
125-
Err(e) => Err(format!("Failed to set default context: {}", e)),
126-
}
127-
}
128-
}
129-
13077
#[uucore::main]
13178
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
13279
let mut args = args.collect_lossy();
@@ -373,7 +320,9 @@ fn create_dir(
373320
// Apply SELinux context if requested
374321
#[cfg(target_os = "linux")]
375322
if set_selinux_context {
376-
if let Err(e) = set_selinux_security_context(path, context) {
323+
if let Err(e) = uucore::selinux_support::set_selinux_security_context(path, context)
324+
{
325+
let _ = std::fs::remove_dir(path);
377326
return Err(USimpleError::new(
378327
1,
379328
format!("failed to set SELinux security context: {}", e),

src/uucore/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ crc32fast = { workspace = true, optional = true }
5959
regex = { workspace = true, optional = true }
6060
bigdecimal = { workspace = true, optional = true }
6161
num-traits = { workspace = true, optional = true }
62+
selinux = { workspace = true, optional = true }
6263

6364
[target.'cfg(unix)'.dependencies]
6465
walkdir = { workspace = true, optional = true }
@@ -106,13 +107,14 @@ format = [
106107
mode = ["libc"]
107108
perms = ["entries", "libc", "walkdir"]
108109
buf-copy = []
110+
parser = ["extendedbigdecimal", "glob", "num-traits"]
109111
pipes = []
110112
process = ["libc"]
111113
proc-info = ["tty", "walkdir"]
112114
quoting-style = []
113115
ranges = []
114116
ringbuffer = []
115-
parser = ["extendedbigdecimal", "glob", "num-traits"]
117+
selinux = ["dep:selinux"]
116118
signals = []
117119
sum = [
118120
"digest",

src/uucore/src/lib/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub mod tty;
6666

6767
#[cfg(all(unix, feature = "fsxattr"))]
6868
pub mod fsxattr;
69+
#[cfg(all(target_os = "linux", feature = "selinux"))]
70+
pub mod selinux;
6971
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
7072
pub mod signals;
7173
#[cfg(all(
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// This file is part of the uutils uucore package.
2+
//
3+
// For the full copyright and license information, please view the LICENSE
4+
// file that was distributed with this source code.
5+
6+
use std::path::Path;
7+
8+
use selinux::SecurityContext;
9+
10+
/// Sets the SELinux security context for the given filesystem path.
11+
///
12+
/// If a specific context is provided, it attempts to set this context explicitly.
13+
/// Otherwise, it applies the default SELinux context for the provided path.
14+
///
15+
/// # Arguments
16+
///
17+
/// * `path` - Filesystem path on which to set the SELinux context.
18+
/// * `context` - Optional SELinux context string to explicitly set.
19+
///
20+
/// # Errors
21+
///
22+
/// Returns an error if:
23+
/// - SELinux is not enabled on the system.
24+
/// - The provided context is invalid or cannot be applied.
25+
/// - The default SELinux context cannot be set.
26+
pub fn set_selinux_security_context(path: &Path, context: Option<&String>) -> Result<(), String> {
27+
// Check if SELinux is enabled on the system
28+
if selinux::kernel_support() == selinux::KernelSupport::Unsupported {
29+
return Err("SELinux is not enabled on this system".into());
30+
}
31+
32+
if let Some(ctx_str) = context {
33+
// Create a CString from the provided context string
34+
let c_context = std::ffi::CString::new(ctx_str.as_str())
35+
.map_err(|_| "Invalid context string (contains null bytes)".to_string())?;
36+
37+
// Convert the CString into an SELinux security context
38+
let security_context = selinux::OpaqueSecurityContext::from_c_str(&c_context)
39+
.map_err(|e| format!("Failed to create security context: {}", e))?;
40+
41+
// Set the provided security context on the specified path
42+
SecurityContext::from_c_str(
43+
&security_context.to_c_string().map_err(|e| e.to_string())?,
44+
false,
45+
)
46+
.set_for_path(path, false, false)
47+
.map_err(|e| format!("Failed to set context: {}", e))
48+
} else {
49+
// If no context provided, set the default SELinux context for the path
50+
SecurityContext::set_default_for_path(path)
51+
.map_err(|e| format!("Failed to set default context: {}", e))
52+
}
53+
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use super::*;
58+
use tempfile::NamedTempFile;
59+
60+
#[test]
61+
fn test_selinux_context_setting() {
62+
let tmpfile = NamedTempFile::new().expect("Failed to create tempfile");
63+
let path = tmpfile.path();
64+
65+
let result = set_selinux_security_context(path, None);
66+
67+
if result.is_ok() {
68+
// SELinux enabled and successfully set default context
69+
assert!(true, "Successfully set SELinux context");
70+
} else {
71+
let err = result.unwrap_err();
72+
let valid_errors = [
73+
"SELinux is not enabled on this system",
74+
&format!(
75+
"Failed to set default context: selinux_lsetfilecon_default() failed on path '{}'",
76+
path.display()
77+
),
78+
];
79+
80+
assert!(
81+
valid_errors.contains(&err.as_str()),
82+
"Unexpected error message: {}",
83+
err
84+
);
85+
}
86+
}
87+
88+
#[test]
89+
fn test_invalid_context_string_error() {
90+
let tmpfile = NamedTempFile::new().expect("Failed to create tempfile");
91+
let path = tmpfile.path();
92+
93+
// Pass a context string containing a null byte to trigger CString::new error
94+
let invalid_context = String::from("invalid\0context");
95+
let result = set_selinux_security_context(path, Some(&invalid_context));
96+
97+
assert!(result.is_err());
98+
assert_eq!(
99+
result.unwrap_err(),
100+
"Invalid context string (contains null bytes)"
101+
);
102+
}
103+
}

src/uucore/src/lib/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub use crate::features::fsext;
103103
#[cfg(all(unix, feature = "fsxattr"))]
104104
pub use crate::features::fsxattr;
105105

106+
#[cfg(all(target_os = "linux", feature = "selinux"))]
107+
pub use crate::features::selinux;
108+
106109
//## core functions
107110

108111
#[cfg(unix)]

0 commit comments

Comments
 (0)