Skip to content

Commit 809e75c

Browse files
authored
Merge pull request #8401 from drinkcat/selinux-fifo-symlinks
selinux: Fix get_context on fifos (and symlinks)
2 parents 8236bcf + 49b2466 commit 809e75c

File tree

2 files changed

+79
-14
lines changed

2 files changed

+79
-14
lines changed

src/uu/stat/src/stat.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,7 @@ impl Stater {
920920
ret
921921
}
922922

923+
#[allow(clippy::too_many_arguments)]
923924
fn process_token_files(
924925
&self,
925926
t: &Token,
@@ -928,6 +929,7 @@ impl Stater {
928929
file: &OsString,
929930
file_type: &FileType,
930931
from_user: bool,
932+
_follow_symbolic_links: bool,
931933
) -> Result<(), i32> {
932934
match *t {
933935
Token::Byte(byte) => write_raw_byte(byte),
@@ -956,8 +958,10 @@ impl Stater {
956958
#[cfg(feature = "selinux")]
957959
{
958960
if uucore::selinux::is_selinux_enabled() {
959-
match uucore::selinux::get_selinux_security_context(Path::new(file))
960-
{
961+
match uucore::selinux::get_selinux_security_context(
962+
Path::new(file),
963+
_follow_symbolic_links,
964+
) {
961965
Ok(ctx) => OutputType::Str(ctx),
962966
Err(_) => OutputType::Str(get_message(
963967
"stat-selinux-failed-get-context",
@@ -1113,7 +1117,8 @@ impl Stater {
11131117
}
11141118
}
11151119
} else {
1116-
let result = if self.follow || stdin_is_fifo && display_name == "-" {
1120+
let follow_symbolic_links = self.follow || stdin_is_fifo && display_name == "-";
1121+
let result = if follow_symbolic_links {
11171122
fs::metadata(&file)
11181123
} else {
11191124
fs::symlink_metadata(&file)
@@ -1137,6 +1142,7 @@ impl Stater {
11371142
&file,
11381143
&file_type,
11391144
self.from_user,
1145+
follow_symbolic_links,
11401146
) {
11411147
return code;
11421148
}

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

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ pub fn set_selinux_security_context(
182182
/// use uucore::selinux::{get_selinux_security_context, SeLinuxError};
183183
///
184184
/// // Get the SELinux context for a file
185-
/// match get_selinux_security_context(Path::new("/path/to/file")) {
185+
/// match get_selinux_security_context(Path::new("/path/to/file"), false) {
186186
/// Ok(context) => {
187187
/// if context.is_empty() {
188188
/// println!("No SELinux context found for the file");
@@ -197,16 +197,16 @@ pub fn set_selinux_security_context(
197197
/// Err(SeLinuxError::ContextSetFailure(ctx, e)) => println!("Failed to set context '{ctx}': {e}"),
198198
/// }
199199
/// ```
200-
pub fn get_selinux_security_context(path: &Path) -> Result<String, SeLinuxError> {
200+
pub fn get_selinux_security_context(
201+
path: &Path,
202+
follow_symbolic_links: bool,
203+
) -> Result<String, SeLinuxError> {
201204
if !is_selinux_enabled() {
202205
return Err(SeLinuxError::SELinuxNotEnabled);
203206
}
204207

205-
let f = std::fs::File::open(path)
206-
.map_err(|e| SeLinuxError::FileOpenFailure(selinux_error_description(&e)))?;
207-
208208
// Get the security context of the file
209-
let context = match SecurityContext::of_file(&f, false) {
209+
let context = match SecurityContext::of_path(path, follow_symbolic_links, false) {
210210
Ok(Some(ctx)) => ctx,
211211
Ok(None) => return Ok(String::new()), // No context found, return empty string
212212
Err(e) => {
@@ -317,7 +317,7 @@ pub fn preserve_security_context(from_path: &Path, to_path: &Path) -> Result<(),
317317
}
318318

319319
// Get context from the source path
320-
let context = get_selinux_security_context(from_path)?;
320+
let context = get_selinux_security_context(from_path, false)?;
321321

322322
// If no context was found, just return success (nothing to preserve)
323323
if context.is_empty() {
@@ -357,7 +357,7 @@ mod tests {
357357
default_result.err()
358358
);
359359

360-
let context = get_selinux_security_context(path).expect("Failed to get context");
360+
let context = get_selinux_security_context(path, false).expect("Failed to get context");
361361
assert!(
362362
!context.is_empty(),
363363
"Expected non-empty context after setting default context"
@@ -367,7 +367,7 @@ mod tests {
367367
let explicit_result = set_selinux_security_context(path, Some(&test_context));
368368

369369
if explicit_result.is_ok() {
370-
let new_context = get_selinux_security_context(path)
370+
let new_context = get_selinux_security_context(path, false)
371371
.expect("Failed to get context after setting explicit context");
372372

373373
assert!(
@@ -420,7 +420,7 @@ mod tests {
420420
}
421421
std::fs::write(path, b"test content").expect("Failed to write to tempfile");
422422

423-
let result = get_selinux_security_context(path);
423+
let result = get_selinux_security_context(path, false);
424424

425425
if result.is_ok() {
426426
let context = result.unwrap();
@@ -484,11 +484,70 @@ mod tests {
484484
println!("test skipped: Kernel has no support for SElinux context");
485485
return;
486486
}
487-
let result = get_selinux_security_context(path);
487+
let result = get_selinux_security_context(path, false);
488488

489489
assert!(result.is_err());
490490
}
491491

492+
#[test]
493+
fn test_get_selinux_context_symlink() {
494+
use std::os::unix::fs::symlink;
495+
use tempfile::tempdir;
496+
497+
if !is_selinux_enabled() {
498+
println!("test skipped: Kernel has no support for SElinux context");
499+
return;
500+
}
501+
502+
let tmp_dir = tempdir().expect("Failed to create temporary directory");
503+
let dir_path = tmp_dir.path();
504+
505+
// Create a normal file
506+
let file_path = dir_path.join("file");
507+
std::fs::File::create(&file_path).expect("Failed to create file");
508+
509+
// Create a symlink to the file
510+
let symlink_path = dir_path.join("symlink");
511+
symlink(&file_path, &symlink_path).expect("Failed to create symlink");
512+
513+
// Set a different context for the file (but not the symlink)
514+
let file_context = String::from("system_u:object_r:user_tmp_t:s0");
515+
set_selinux_security_context(&file_path, Some(&file_context))
516+
.expect("Failed to set security context.");
517+
518+
// Context must be different if we don't follow the link
519+
let file_context = get_selinux_security_context(&file_path, false)
520+
.expect("Failed to get security context.");
521+
let symlink_context = get_selinux_security_context(&symlink_path, false)
522+
.expect("Failed to get security context.");
523+
assert_ne!(file_context.to_string(), symlink_context.to_string());
524+
525+
// Context must be the same if we follow the link
526+
let symlink_follow_context = get_selinux_security_context(&symlink_path, true)
527+
.expect("Failed to get security context.");
528+
assert_eq!(file_context.to_string(), symlink_follow_context.to_string());
529+
}
530+
531+
#[test]
532+
fn test_get_selinux_context_fifo() {
533+
use tempfile::tempdir;
534+
535+
if !is_selinux_enabled() {
536+
println!("test skipped: Kernel has no support for SElinux context");
537+
return;
538+
}
539+
540+
let tmp_dir = tempdir().expect("Failed to create temporary directory");
541+
let dir_path = tmp_dir.path();
542+
543+
// Create a FIFO (pipe)
544+
let fifo_path = dir_path.join("my_fifo");
545+
crate::fs::make_fifo(&fifo_path).expect("Failed to create FIFO");
546+
547+
// Just getting a context is good enough
548+
get_selinux_security_context(&fifo_path, false).expect("Cannot get fifo context");
549+
}
550+
492551
#[test]
493552
fn test_contexts_differ() {
494553
let file1 = NamedTempFile::new().expect("Failed to create first tempfile");

0 commit comments

Comments
 (0)