Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/uucore/src/lib/features/selinux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
56 changes: 56 additions & 0 deletions tests/by-util/test_mkdir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mknod and mkfifo go through the same code, please add a test for one of them too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, added one for mkfifo.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as you said, this passes without the fix under /tmp, so CI won't catch a regression
could you use a dir the policy has a default for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The tests now run in target/tmp, which is under the home dir in the SELinux job, so the policy has a default there. They fail without the fix in a Fedora VM set up like that job, and skip where the policy has no default.

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.
Expand Down
48 changes: 48 additions & 0 deletions tests/by-util/test_mkfifo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading