Skip to content

Commit 02be974

Browse files
committed
set_selinux_security_context: also display the error from the crate
+ fix comments from review
1 parent beab72d commit 02be974

File tree

5 files changed

+145
-67
lines changed

5 files changed

+145
-67
lines changed

src/uu/cp/src/cp.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2458,7 +2458,11 @@ fn copy_file(
24582458
#[cfg(feature = "selinux")]
24592459
if options.set_selinux_context && uucore::selinux::is_selinux_enabled() {
24602460
// Set the given selinux permissions on the copied file.
2461-
uucore::selinux::set_selinux_security_context(dest, options.context.as_ref())?;
2461+
if let Err(e) =
2462+
uucore::selinux::set_selinux_security_context(dest, options.context.as_ref())
2463+
{
2464+
return Err(Error::Error(format!("SELinux error: {}", e)));
2465+
}
24622466
}
24632467

24642468
copied_files.insert(

src/uu/mkdir/src/mkdir.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,7 @@ fn create_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<()> {
298298
if let Err(e) = uucore::selinux::set_selinux_security_context(path, config.context)
299299
{
300300
let _ = std::fs::remove_dir(path);
301-
return Err(USimpleError::new(
302-
1,
303-
format!("failed to set SELinux security context: {}", e),
304-
));
301+
return Err(USimpleError::new(1, e.to_string()));
305302
}
306303
}
307304

src/uucore/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ proc-info = ["tty", "walkdir"]
114114
quoting-style = []
115115
ranges = []
116116
ringbuffer = []
117-
selinux = ["dep:selinux"]
117+
selinux = ["dep:selinux", "thiserror"]
118118
signals = []
119119
sum = [
120120
"digest",

src/uucore/src/lib/features/selinux.rs

Lines changed: 137 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,27 @@ pub enum SeLinuxError {
1313
#[error("SELinux is not enabled on this system")]
1414
SELinuxNotEnabled,
1515

16-
#[error("Failed to open the file")]
17-
FileOpenFailure,
16+
#[error("Failed to open the file: {0}")]
17+
FileOpenFailure(String),
1818

19-
#[error("Failed to retrieve the security context")]
20-
ContextRetrievalFailure,
19+
#[error("Failed to retrieve the security context: {0}")]
20+
ContextRetrievalFailure(String),
2121

22-
#[error("failed to set default file creation context to {0}")]
23-
ContextSetFailure(String),
22+
#[error("Failed to set default file creation context to '{0}': {1}")]
23+
ContextSetFailure(String, String),
2424

25-
#[error("Invalid context string or conversion failure")]
26-
ContextConversionFailure,
25+
#[error("Failed to set default file creation context to '{0}': {1}")]
26+
ContextConversionFailure(String, String),
2727
}
2828

2929
impl From<SeLinuxError> for i32 {
3030
fn from(error: SeLinuxError) -> i32 {
3131
match error {
3232
SeLinuxError::SELinuxNotEnabled => 1,
33-
SeLinuxError::FileOpenFailure => 2,
34-
SeLinuxError::ContextRetrievalFailure => 3,
35-
SeLinuxError::ContextSetFailure(_) => 4,
36-
SeLinuxError::ContextConversionFailure => 5,
33+
SeLinuxError::FileOpenFailure(_) => 2,
34+
SeLinuxError::ContextRetrievalFailure(_) => 3,
35+
SeLinuxError::ContextSetFailure(_, _) => 4,
36+
SeLinuxError::ContextConversionFailure(_, _) => 5,
3737
}
3838
}
3939
}
@@ -98,26 +98,29 @@ pub fn set_selinux_security_context(
9898

9999
if let Some(ctx_str) = context {
100100
// Create a CString from the provided context string
101-
let c_context = std::ffi::CString::new(ctx_str.as_str())
102-
.map_err(|_| SeLinuxError::ContextConversionFailure)?;
101+
let c_context = std::ffi::CString::new(ctx_str.as_str()).map_err(|e| {
102+
SeLinuxError::ContextConversionFailure(ctx_str.to_string(), e.to_string())
103+
})?;
103104

104105
// Convert the CString into an SELinux security context
105-
let security_context = selinux::OpaqueSecurityContext::from_c_str(&c_context)
106-
.map_err(|_| SeLinuxError::ContextConversionFailure)?;
106+
let security_context =
107+
selinux::OpaqueSecurityContext::from_c_str(&c_context).map_err(|e| {
108+
SeLinuxError::ContextConversionFailure(ctx_str.to_string(), e.to_string())
109+
})?;
107110

108111
// Set the provided security context on the specified path
109112
SecurityContext::from_c_str(
110-
&security_context
111-
.to_c_string()
112-
.map_err(|_| SeLinuxError::ContextConversionFailure)?,
113+
&security_context.to_c_string().map_err(|e| {
114+
SeLinuxError::ContextConversionFailure(ctx_str.to_string(), e.to_string())
115+
})?,
113116
false,
114117
)
115118
.set_for_path(path, false, false)
116-
.map_err(|_| SeLinuxError::ContextSetFailure(ctx_str.to_string()))
119+
.map_err(|e| SeLinuxError::ContextSetFailure(ctx_str.to_string(), e.to_string()))
117120
} else {
118121
// If no context provided, set the default SELinux context for the path
119122
SecurityContext::set_default_for_path(path)
120-
.map_err(|_| SeLinuxError::ContextSetFailure("".to_string()))
123+
.map_err(|e| SeLinuxError::ContextSetFailure(String::new(), e.to_string()))
121124
}
122125
}
123126

@@ -157,28 +160,29 @@ pub fn set_selinux_security_context(
157160
/// }
158161
/// },
159162
/// Err(SeLinuxError::SELinuxNotEnabled) => println!("SELinux is not enabled on this system"),
160-
/// Err(SeLinuxError::FileOpenFailure) => println!("Failed to open the file"),
161-
/// Err(SeLinuxError::ContextRetrievalFailure) => println!("Failed to retrieve the security context"),
162-
/// Err(SeLinuxError::ContextConversionFailure) => println!("Failed to convert the security context to a string"),
163+
/// Err(SeLinuxError::FileOpenFailure(e)) => println!("Failed to open the file: {}", e),
164+
/// Err(SeLinuxError::ContextRetrievalFailure(e)) => println!("Failed to retrieve the security context: {}", e),
165+
/// Err(SeLinuxError::ContextConversionFailure(ctx, e)) => println!("Failed to convert context '{}': {}", ctx, e),
166+
/// Err(SeLinuxError::ContextSetFailure(ctx, e)) => println!("Failed to set context '{}': {}", ctx, e),
163167
/// }
164168
/// ```
165169
pub fn get_selinux_security_context(path: &Path) -> Result<String, SeLinuxError> {
166170
if !is_selinux_enabled() {
167171
return Err(SeLinuxError::SELinuxNotEnabled);
168172
}
169173

170-
let f = std::fs::File::open(path).map_err(|_| SeLinuxError::FileOpenFailure)?;
174+
let f = std::fs::File::open(path).map_err(|e| SeLinuxError::FileOpenFailure(e.to_string()))?;
171175

172176
// Get the security context of the file
173177
let context = match SecurityContext::of_file(&f, false) {
174178
Ok(Some(ctx)) => ctx,
175179
Ok(None) => return Ok(String::new()), // No context found, return empty string
176-
Err(_) => return Err(SeLinuxError::ContextRetrievalFailure),
180+
Err(e) => return Err(SeLinuxError::ContextRetrievalFailure(e.to_string())),
177181
};
178182

179183
let context_c_string = context
180184
.to_c_string()
181-
.map_err(|_| SeLinuxError::ContextConversionFailure)?;
185+
.map_err(|e| SeLinuxError::ContextConversionFailure(String::new(), e.to_string()))?;
182186

183187
if let Some(c_str) = context_c_string {
184188
// Convert the C string to a Rust String
@@ -198,29 +202,50 @@ mod tests {
198202
let tmpfile = NamedTempFile::new().expect("Failed to create tempfile");
199203
let path = tmpfile.path();
200204

201-
let result = set_selinux_security_context(path, None);
205+
if !is_selinux_enabled() {
206+
let result = set_selinux_security_context(path, None);
207+
assert!(result.is_err(), "Expected error when SELinux is disabled");
208+
match result.unwrap_err() {
209+
SeLinuxError::SELinuxNotEnabled => {
210+
// This is the expected error when SELinux is not enabled
211+
}
212+
err => panic!("Expected SELinuxNotEnabled error but got: {}", err),
213+
}
214+
return;
215+
}
202216

203-
if result.is_ok() {
204-
// SELinux enabled and successfully set default context
205-
assert!(true, "Successfully set SELinux context");
206-
} else {
207-
let err = result.unwrap_err();
208-
let valid_errors = [
209-
"SELinux is not enabled on this system",
210-
&format!(
211-
"Failed to set default context: selinux_lsetfilecon_default() failed on path '{}'",
212-
path.display()
213-
),
214-
];
217+
let default_result = set_selinux_security_context(path, None);
218+
assert!(
219+
default_result.is_ok(),
220+
"Failed to set default context: {:?}",
221+
default_result.err()
222+
);
223+
224+
let context = get_selinux_security_context(path).expect("Failed to get context");
225+
assert!(
226+
!context.is_empty(),
227+
"Expected non-empty context after setting default context"
228+
);
229+
230+
let test_context = String::from("system_u:object_r:tmp_t:s0");
231+
let explicit_result = set_selinux_security_context(path, Some(&test_context));
232+
233+
if explicit_result.is_ok() {
234+
let new_context = get_selinux_security_context(path)
235+
.expect("Failed to get context after setting explicit context");
215236

216237
assert!(
217-
valid_errors.contains(&err.as_str()),
218-
"Unexpected error message: {}",
219-
err
238+
new_context.contains("tmp_t"),
239+
"Expected context to contain 'tmp_t', but got: {}",
240+
new_context
241+
);
242+
} else {
243+
println!(
244+
"Note: Could not set explicit context {:?}",
245+
explicit_result.err()
220246
);
221247
}
222248
}
223-
224249
#[test]
225250
fn test_invalid_context_string_error() {
226251
let tmpfile = NamedTempFile::new().expect("Failed to create tempfile");
@@ -231,10 +256,18 @@ mod tests {
231256
let result = set_selinux_security_context(path, Some(&invalid_context));
232257

233258
assert!(result.is_err());
234-
assert_eq!(
235-
result.unwrap_err(),
236-
"Invalid context string (contains null bytes)"
237-
);
259+
if let Err(err) = result {
260+
match err {
261+
SeLinuxError::ContextConversionFailure(ctx, msg) => {
262+
assert_eq!(ctx, "invalid\0context");
263+
assert!(
264+
msg.contains("nul byte"),
265+
"Error message should mention nul byte"
266+
);
267+
}
268+
_ => panic!("Expected ContextConversionFailure error but got: {}", err),
269+
}
270+
}
238271
}
239272

240273
#[test]
@@ -261,19 +294,56 @@ mod tests {
261294
let result = get_selinux_security_context(path);
262295

263296
if result.is_ok() {
264-
println!("Retrieved SELinux context: {}", result.unwrap());
297+
let context = result.unwrap();
298+
println!("Retrieved SELinux context: {}", context);
299+
300+
assert!(
301+
is_selinux_enabled(),
302+
"Got a successful context result but SELinux is not enabled"
303+
);
304+
305+
if !context.is_empty() {
306+
assert!(
307+
context.contains(':'),
308+
"SELinux context '{}' doesn't match expected format",
309+
context
310+
);
311+
}
265312
} else {
266313
let err = result.unwrap_err();
267314

268-
// Valid error types
269315
match err {
270-
SeLinuxError::SELinuxNotEnabled => assert!(true, "SELinux not supported"),
271-
SeLinuxError::ContextRetrievalFailure => assert!(true, "Context retrieval failure"),
272-
SeLinuxError::ContextConversionFailure => {
273-
assert!(true, "Context conversion failure")
316+
SeLinuxError::SELinuxNotEnabled => {
317+
assert!(
318+
!is_selinux_enabled(),
319+
"Got SELinuxNotEnabled error, but is_selinux_enabled() returned true"
320+
);
321+
}
322+
SeLinuxError::ContextRetrievalFailure(e) => {
323+
assert!(
324+
is_selinux_enabled(),
325+
"Got ContextRetrievalFailure when SELinux is not enabled"
326+
);
327+
assert!(!e.is_empty(), "Error message should not be empty");
328+
println!("Context retrieval failure: {}", e);
274329
}
275-
SeLinuxError::FileOpenFailure => {
276-
panic!("File open failure occurred despite file being created")
330+
SeLinuxError::ContextConversionFailure(ctx, e) => {
331+
assert!(
332+
is_selinux_enabled(),
333+
"Got ContextConversionFailure when SELinux is not enabled"
334+
);
335+
assert!(!e.is_empty(), "Error message should not be empty");
336+
println!("Context conversion failure for '{}': {}", ctx, e);
337+
}
338+
SeLinuxError::ContextSetFailure(ctx, e) => {
339+
assert!(false);
340+
}
341+
SeLinuxError::FileOpenFailure(e) => {
342+
assert!(
343+
Path::new(path).exists(),
344+
"File open failure occurred despite file being created: {}",
345+
e
346+
);
277347
}
278348
}
279349
}
@@ -286,9 +356,16 @@ mod tests {
286356
let result = get_selinux_security_context(path);
287357

288358
assert!(result.is_err());
289-
assert!(
290-
matches!(result.unwrap_err(), SeLinuxError::FileOpenFailure),
291-
"Expected file open error for nonexistent file"
292-
);
359+
if let Err(err) = result {
360+
match err {
361+
SeLinuxError::FileOpenFailure(e) => {
362+
assert!(
363+
e.contains("No such file"),
364+
"Error should mention file not found"
365+
);
366+
}
367+
_ => panic!("Expected FileOpenFailure error but got: {}", err),
368+
}
369+
}
293370
}
294371
}

tests/by-util/test_mkdir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ fn test_selinux_invalid() {
411411
.arg(at.plus_as_string(dest))
412412
.fails()
413413
.no_stdout()
414-
.stderr_contains("failed to set SELinux security context:");
414+
.stderr_contains("Failed to set default file creation context to 'testtest':");
415415
// invalid context, so, no directory
416416
assert!(!at.dir_exists(dest));
417417
}

0 commit comments

Comments
 (0)