|
3 | 3 | // For the full copyright and license information, please view the LICENSE |
4 | 4 | // file that was distributed with this source code. |
5 | 5 |
|
| 6 | +use std::error::Error; |
6 | 7 | use std::path::Path; |
7 | 8 |
|
8 | 9 | use selinux::SecurityContext; |
@@ -45,6 +46,22 @@ pub fn is_selinux_enabled() -> bool { |
45 | 46 | selinux::kernel_support() != selinux::KernelSupport::Unsupported |
46 | 47 | } |
47 | 48 |
|
| 49 | +/// Returns a string describing the error and its causes. |
| 50 | +fn selinux_error_description(mut error: &dyn Error) -> String { |
| 51 | + let mut description = String::new(); |
| 52 | + while let Some(source) = error.source() { |
| 53 | + let error_text = source.to_string(); |
| 54 | + // Check if this is an OS error and trim it |
| 55 | + if let Some(idx) = error_text.find(" (os error ") { |
| 56 | + description.push_str(&error_text[..idx]); |
| 57 | + } else { |
| 58 | + description.push_str(&error_text); |
| 59 | + } |
| 60 | + error = source; |
| 61 | + } |
| 62 | + description |
| 63 | +} |
| 64 | + |
48 | 65 | /// Sets the SELinux security context for the given filesystem path. |
49 | 66 | /// |
50 | 67 | /// If a specific context is provided, it attempts to set this context explicitly. |
@@ -99,28 +116,40 @@ pub fn set_selinux_security_context( |
99 | 116 | if let Some(ctx_str) = context { |
100 | 117 | // Create a CString from the provided context string |
101 | 118 | let c_context = std::ffi::CString::new(ctx_str.as_str()).map_err(|e| { |
102 | | - SeLinuxError::ContextConversionFailure(ctx_str.to_string(), e.to_string()) |
| 119 | + SeLinuxError::ContextConversionFailure( |
| 120 | + ctx_str.to_string(), |
| 121 | + selinux_error_description(&e), |
| 122 | + ) |
103 | 123 | })?; |
104 | 124 |
|
105 | 125 | // Convert the CString into an SELinux security context |
106 | 126 | let security_context = |
107 | 127 | selinux::OpaqueSecurityContext::from_c_str(&c_context).map_err(|e| { |
108 | | - SeLinuxError::ContextConversionFailure(ctx_str.to_string(), e.to_string()) |
| 128 | + SeLinuxError::ContextConversionFailure( |
| 129 | + ctx_str.to_string(), |
| 130 | + selinux_error_description(&e), |
| 131 | + ) |
109 | 132 | })?; |
110 | 133 |
|
111 | 134 | // Set the provided security context on the specified path |
112 | 135 | SecurityContext::from_c_str( |
113 | 136 | &security_context.to_c_string().map_err(|e| { |
114 | | - SeLinuxError::ContextConversionFailure(ctx_str.to_string(), e.to_string()) |
| 137 | + SeLinuxError::ContextConversionFailure( |
| 138 | + ctx_str.to_string(), |
| 139 | + selinux_error_description(&e), |
| 140 | + ) |
115 | 141 | })?, |
116 | 142 | false, |
117 | 143 | ) |
118 | 144 | .set_for_path(path, false, false) |
119 | | - .map_err(|e| SeLinuxError::ContextSetFailure(ctx_str.to_string(), e.to_string())) |
| 145 | + .map_err(|e| { |
| 146 | + SeLinuxError::ContextSetFailure(ctx_str.to_string(), selinux_error_description(&e)) |
| 147 | + }) |
120 | 148 | } else { |
121 | 149 | // If no context provided, set the default SELinux context for the path |
122 | | - SecurityContext::set_default_for_path(path) |
123 | | - .map_err(|e| SeLinuxError::ContextSetFailure(String::new(), e.to_string())) |
| 150 | + SecurityContext::set_default_for_path(path).map_err(|e| { |
| 151 | + SeLinuxError::ContextSetFailure(String::new(), selinux_error_description(&e)) |
| 152 | + }) |
124 | 153 | } |
125 | 154 | } |
126 | 155 |
|
@@ -171,18 +200,23 @@ pub fn get_selinux_security_context(path: &Path) -> Result<String, SeLinuxError> |
171 | 200 | return Err(SeLinuxError::SELinuxNotEnabled); |
172 | 201 | } |
173 | 202 |
|
174 | | - let f = std::fs::File::open(path).map_err(|e| SeLinuxError::FileOpenFailure(e.to_string()))?; |
| 203 | + let f = std::fs::File::open(path) |
| 204 | + .map_err(|e| SeLinuxError::FileOpenFailure(selinux_error_description(&e)))?; |
175 | 205 |
|
176 | 206 | // Get the security context of the file |
177 | 207 | let context = match SecurityContext::of_file(&f, false) { |
178 | 208 | Ok(Some(ctx)) => ctx, |
179 | 209 | Ok(None) => return Ok(String::new()), // No context found, return empty string |
180 | | - Err(e) => return Err(SeLinuxError::ContextRetrievalFailure(e.to_string())), |
| 210 | + Err(e) => { |
| 211 | + return Err(SeLinuxError::ContextRetrievalFailure( |
| 212 | + selinux_error_description(&e), |
| 213 | + )); |
| 214 | + } |
181 | 215 | }; |
182 | 216 |
|
183 | | - let context_c_string = context |
184 | | - .to_c_string() |
185 | | - .map_err(|e| SeLinuxError::ContextConversionFailure(String::new(), e.to_string()))?; |
| 217 | + let context_c_string = context.to_c_string().map_err(|e| { |
| 218 | + SeLinuxError::ContextConversionFailure(String::new(), selinux_error_description(&e)) |
| 219 | + })?; |
186 | 220 |
|
187 | 221 | if let Some(c_str) = context_c_string { |
188 | 222 | // Convert the C string to a Rust String |
@@ -336,7 +370,8 @@ mod tests { |
336 | 370 | println!("Context conversion failure for '{}': {}", ctx, e); |
337 | 371 | } |
338 | 372 | SeLinuxError::ContextSetFailure(ctx, e) => { |
339 | | - assert!(false); |
| 373 | + assert!(!e.is_empty(), "Error message should not be empty"); |
| 374 | + println!("Context conversion failure for '{}': {}", ctx, e); |
340 | 375 | } |
341 | 376 | SeLinuxError::FileOpenFailure(e) => { |
342 | 377 | assert!( |
|
0 commit comments