diff --git a/src/uucore/src/lib/features/selinux.rs b/src/uucore/src/lib/features/selinux.rs index 4036bf1d95a..21b65258673 100644 --- a/src/uucore/src/lib/features/selinux.rs +++ b/src/uucore/src/lib/features/selinux.rs @@ -146,8 +146,9 @@ impl FsCreateContext { SeLinuxError::ContextRetrievalFailure(selinux_error_description(&e)) })?; + let path = absolute_for_policy_lookup(path); let default_context = - match labeler.look_up_by_path(path, mode.and_then(selinux::FileAccessMode::new)) { + match labeler.look_up_by_path(&path, mode.and_then(selinux::FileAccessMode::new)) { Ok(context) => context, Err(e) if is_ignorable_context_error(&e) => { return Ok(Self { diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index b12e90c280e..d3ffaff868a 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -594,6 +594,62 @@ fn test_selinux_invalid() { assert!(!at.dir_exists(dest)); } +// -Z looks the name up in the policy, which lists absolute names only: a relative +// name must get the same label as the absolute one. +#[test] +#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))] +fn test_selinux_default_context_relative_name() { + use uucore::selinux::{ + get_selinux_security_context, is_selinux_enabled, set_selinux_security_context, + }; + use uutests::util::AtPath; + + if !is_selinux_enabled() { + println!("Skipping SELinux test: SELinux is not enabled"); + return; + } + // The policy has no default under /tmp, so both names would inherit there; + // target/tmp is usually under $HOME, which has one. + let dir = tempfile::tempdir_in(env!("CARGO_TARGET_TMPDIR")).unwrap(); + let at = AtPath::new(dir.path()); + let scene = TestScenario::new(util_name!()); + // A type the policy won't hand out here, so a missed lookup shows as inheritance. + at.mkdir("parent"); + let ctx = String::from("root:object_r:etc_t:s0"); + if set_selinux_security_context(&at.plus("parent"), Some(&ctx)).is_err() { + println!("Skipping SELinux test: cannot set a context on the fixture"); + return; + } + let type_of = |p: &str| { + let ctx = get_selinux_security_context(&at.plus(p), false).unwrap(); + ctx.split(':').nth(2).unwrap().to_owned() + }; + + scene + .ucmd() + .args(&["-Z", &at.plus_as_string("parent/absolute")]) + .succeeds(); + let expected = type_of("parent/absolute"); + if expected == "etc_t" { + println!("Skipping SELinux test: the policy has no default for {dir:?}"); + return; + } + scene + .ucmd() + .current_dir(dir.path()) + .args(&["-Z", "parent/relative"]) + .succeeds(); + scene + .ucmd() + .current_dir(dir.path()) + .args(&["-p", "-Z", "parent/new/relative"]) + .succeeds(); + + for name in ["parent/relative", "parent/new", "parent/new/relative"] { + assert_eq!(type_of(name), expected, "{name}"); + } +} + #[test] fn test_mkdir_deep_nesting() { // Regression test for stack overflow with deeply nested directories. diff --git a/tests/by-util/test_mkfifo.rs b/tests/by-util/test_mkfifo.rs index d2b41f72415..58b93d358f1 100644 --- a/tests/by-util/test_mkfifo.rs +++ b/tests/by-util/test_mkfifo.rs @@ -215,6 +215,54 @@ fn test_mkfifo_selinux_invalid() { } } +// -Z looks the name up in the policy, which lists absolute names only: a relative +// name must get the same label as the absolute one. +#[test] +#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))] +fn test_mkfifo_selinux_default_context_relative_name() { + use uucore::selinux::{ + get_selinux_security_context, is_selinux_enabled, set_selinux_security_context, + }; + use uutests::util::AtPath; + + if !is_selinux_enabled() { + println!("Skipping SELinux test: SELinux is not enabled"); + return; + } + // The policy has no default under /tmp, so both names would inherit there; + // target/tmp is usually under $HOME, which has one. + let dir = tempfile::tempdir_in(env!("CARGO_TARGET_TMPDIR")).unwrap(); + let at = AtPath::new(dir.path()); + let scene = TestScenario::new(util_name!()); + // A type the policy won't hand out here, so a missed lookup shows as inheritance. + at.mkdir("parent"); + let ctx = String::from("root:object_r:etc_t:s0"); + if set_selinux_security_context(&at.plus("parent"), Some(&ctx)).is_err() { + println!("Skipping SELinux test: cannot set a context on the fixture"); + return; + } + let type_of = |p: &str| { + let ctx = get_selinux_security_context(&at.plus(p), false).unwrap(); + ctx.split(':').nth(2).unwrap().to_owned() + }; + + scene + .ucmd() + .args(&["-Z", &at.plus_as_string("parent/absolute")]) + .succeeds(); + let expected = type_of("parent/absolute"); + if expected == "etc_t" { + println!("Skipping SELinux test: the policy has no default for {dir:?}"); + return; + } + scene + .ucmd() + .current_dir(dir.path()) + .args(&["-Z", "parent/relative"]) + .succeeds(); + assert_eq!(type_of("parent/relative"), expected); +} + #[test] fn test_mkfifo_permission_unchanged_when_failed() { use uucore::fs::display_permissions;