@@ -10,6 +10,20 @@ use selinux::SecurityContext;
10
10
#[ derive( Debug ) ]
11
11
pub enum Error {
12
12
SELinuxNotEnabled ,
13
+ FileOpenFailure ,
14
+ ContextRetrievalFailure ,
15
+ ContextConversionFailure ,
16
+ }
17
+
18
+ impl From < Error > for i32 {
19
+ fn from ( error : Error ) -> i32 {
20
+ match error {
21
+ Error :: SELinuxNotEnabled => 1 ,
22
+ Error :: FileOpenFailure => 2 ,
23
+ Error :: ContextRetrievalFailure => 3 ,
24
+ Error :: ContextConversionFailure => 4 ,
25
+ }
26
+ }
13
27
}
14
28
15
29
/// Checks if SELinux is enabled on the system.
@@ -109,6 +123,73 @@ pub fn set_selinux_security_context(path: &Path, context: Option<&String>) -> Re
109
123
}
110
124
}
111
125
126
+ /// Gets the SELinux security context for the given filesystem path.
127
+ ///
128
+ /// Retrieves the security context of the specified filesystem path if SELinux is enabled
129
+ /// on the system.
130
+ ///
131
+ /// # Arguments
132
+ ///
133
+ /// * `path` - Filesystem path for which to retrieve the SELinux context.
134
+ ///
135
+ /// # Returns
136
+ ///
137
+ /// * `Ok(String)` - The SELinux context string if successfully retrieved. Returns an empty
138
+ /// string if no context was found.
139
+ /// * `Err(Error)` - An error variant indicating the type of failure:
140
+ /// - `Error::SELinuxNotEnabled` - SELinux is not enabled on the system.
141
+ /// - `Error::FileOpenFailure` - Failed to open the specified file.
142
+ /// - `Error::ContextRetrievalFailure` - Failed to retrieve the security context.
143
+ /// - `Error::ContextConversionFailure` - Failed to convert the security context to a string.
144
+ ///
145
+ /// # Examples
146
+ ///
147
+ /// ```
148
+ /// use std::path::Path;
149
+ /// use uucore::selinux::{get_selinux_security_context, Error};
150
+ ///
151
+ /// // Get the SELinux context for a file
152
+ /// match get_selinux_security_context(Path::new("/path/to/file")) {
153
+ /// Ok(context) => {
154
+ /// if context.is_empty() {
155
+ /// println!("No SELinux context found for the file");
156
+ /// } else {
157
+ /// println!("SELinux context: {}", context);
158
+ /// }
159
+ /// },
160
+ /// Err(Error::SELinuxNotEnabled) => println!("SELinux is not enabled on this system"),
161
+ /// Err(Error::FileOpenFailure) => println!("Failed to open the file"),
162
+ /// Err(Error::ContextRetrievalFailure) => println!("Failed to retrieve the security context"),
163
+ /// Err(Error::ContextConversionFailure) => println!("Failed to convert the security context to a string"),
164
+ /// }
165
+ /// ```
166
+
167
+ pub fn get_selinux_security_context ( path : & Path ) -> Result < String , Error > {
168
+ if selinux:: kernel_support ( ) == selinux:: KernelSupport :: Unsupported {
169
+ return Err ( Error :: SELinuxNotEnabled ) ;
170
+ }
171
+
172
+ let f = std:: fs:: File :: open ( path) . map_err ( |_| Error :: FileOpenFailure ) ?;
173
+
174
+ // Get the security context of the file
175
+ let context = match SecurityContext :: of_file ( & f, false ) {
176
+ Ok ( Some ( ctx) ) => ctx,
177
+ Ok ( None ) => return Ok ( String :: new ( ) ) , // No context found, return empty string
178
+ Err ( _) => return Err ( Error :: ContextRetrievalFailure ) ,
179
+ } ;
180
+
181
+ let context_c_string = context
182
+ . to_c_string ( )
183
+ . map_err ( |_| Error :: ContextConversionFailure ) ?;
184
+
185
+ if let Some ( c_str) = context_c_string {
186
+ // Convert the C string to a Rust String
187
+ Ok ( c_str. to_string_lossy ( ) . to_string ( ) )
188
+ } else {
189
+ Ok ( String :: new ( ) )
190
+ }
191
+ }
192
+
112
193
#[ cfg( test) ]
113
194
mod tests {
114
195
use super :: * ;
@@ -171,4 +252,43 @@ mod tests {
171
252
}
172
253
}
173
254
}
255
+
256
+ #[ test]
257
+ fn test_get_selinux_security_context ( ) {
258
+ let tmpfile = NamedTempFile :: new ( ) . expect ( "Failed to create tempfile" ) ;
259
+ let path = tmpfile. path ( ) ;
260
+
261
+ std:: fs:: write ( path, b"test content" ) . expect ( "Failed to write to tempfile" ) ;
262
+
263
+ let result = get_selinux_security_context ( path) ;
264
+
265
+ if result. is_ok ( ) {
266
+ println ! ( "Retrieved SELinux context: {}" , result. unwrap( ) ) ;
267
+ } else {
268
+ let err = result. unwrap_err ( ) ;
269
+
270
+ // Valid error types
271
+ match err {
272
+ Error :: SELinuxNotEnabled => assert ! ( true , "SELinux not supported" ) ,
273
+ Error :: ContextRetrievalFailure => assert ! ( true , "Context retrieval failure" ) ,
274
+ Error :: ContextConversionFailure => assert ! ( true , "Context conversion failure" ) ,
275
+ Error :: FileOpenFailure => {
276
+ panic ! ( "File open failure occurred despite file being created" )
277
+ }
278
+ }
279
+ }
280
+ }
281
+
282
+ #[ test]
283
+ fn test_get_selinux_context_nonexistent_file ( ) {
284
+ let path = Path :: new ( "/nonexistent/file/that/does/not/exist" ) ;
285
+
286
+ let result = get_selinux_security_context ( path) ;
287
+
288
+ assert ! ( result. is_err( ) ) ;
289
+ assert ! (
290
+ matches!( result. unwrap_err( ) , Error :: FileOpenFailure ) ,
291
+ "Expected file open error for nonexistent file"
292
+ ) ;
293
+ }
174
294
}
0 commit comments