From d4743eb4ffcf422b3e0628c79e3ad5dad0ce1bfd Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sun, 4 Feb 2024 17:44:52 -0700 Subject: [PATCH] Convert chflags/09.t This test asserts that chflags will fail if securelevel=1 and certain flags are already set. Since it requires changing securelevel, we do it in a jail, using the jail-rs crate. Since we can't fork in a multithreaded program, we instead execute /bin/chflags within the jail. Issue #10 --- rust/Cargo.lock | 140 ++++++++++++++++++++++++++++++++++++-- rust/Cargo.toml | 3 + rust/src/context.rs | 16 +++++ rust/src/tests/chflags.rs | 38 +++++++++++ 4 files changed, 190 insertions(+), 7 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7b65095e..f48ba704 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -51,6 +51,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -150,6 +159,15 @@ dependencies = [ "syn", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.0" @@ -166,6 +184,24 @@ dependencies = [ "ghost", ] +[[package]] +name = "jail" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36953efd4638596969dd872a4266e025bc61dafea39ab5531aa90564dda8ab18" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "libc", + "log", + "nix 0.22.3", + "rctl", + "strum 0.21.0", + "strum_macros 0.21.1", + "sysctl 0.4.6", + "thiserror", +] + [[package]] name = "libc" version = "0.2.153" @@ -187,6 +223,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.7.1" @@ -196,6 +241,19 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "nix" version = "0.26.4" @@ -205,10 +263,16 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "once_cell" version = "1.16.0" @@ -237,14 +301,15 @@ dependencies = [ "figment", "gumdrop", "inventory", - "nix", + "jail", + "nix 0.26.4", "once_cell", "paste", "rand", "serde", - "strum", - "strum_macros", - "sysctl", + "strum 0.24.1", + "strum_macros 0.24.3", + "sysctl 0.5.2", "tempfile", "walkdir", ] @@ -303,6 +368,20 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rctl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6bfd2709ced4ee4c712acb5aa08dc3580c78529434d4c40e9687127247a32a6" +dependencies = [ + "libc", + "nix 0.22.3", + "number_prefix", + "sysctl 0.4.6", + "thiserror", + "users", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -366,13 +445,31 @@ dependencies = [ "syn", ] +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" + [[package]] name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", +] + +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -381,7 +478,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.0", "proc-macro2", "quote", "rustversion", @@ -399,6 +496,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysctl" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "libc", + "thiserror", + "walkdir", +] + [[package]] name = "sysctl" version = "0.5.2" @@ -469,6 +579,22 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + [[package]] name = "uuid" version = "1.2.1" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index cdf27fcb..b35f97c1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -20,6 +20,9 @@ inventory = "0.3.0" walkdir = "2.3.2" sysctl = "0.5.2" +[target.'cfg(target_os = "freebsd")'.dependencies] +jail = "0.2.0" + [target.'cfg(target_os = "linux")'.dependencies] caps = "0.5.4" diff --git a/rust/src/context.rs b/rust/src/context.rs index bac67bf0..8aeb9144 100644 --- a/rust/src/context.rs +++ b/rust/src/context.rs @@ -77,6 +77,8 @@ pub struct TestContext<'a> { temp_dir: &'a Path, features_config: &'a FeaturesConfig, auth_entries: DummyAuthEntries<'a>, + #[cfg(target_os = "freebsd")] + jail: Option } pub struct SerializedTestContext<'a> { @@ -162,6 +164,8 @@ impl<'a> TestContext<'a> { temp_dir, features_config: &config.features, auth_entries: DummyAuthEntries::new(entries), + #[cfg(target_os = "freebsd")] + jail: None } } @@ -275,6 +279,12 @@ impl<'a> TestContext<'a> { pub fn nap(&self) { thread::sleep(self.naptime) } + + /// Set this Context's jail, so it will be destroyed during teardown. + #[cfg(target_os = "freebsd")] + pub fn set_jail(&mut self, jail: jail::RunningJail) { + self.jail = Some(jail) + } } // We implement Drop to circumvent the errors which arise from unlinking a directory for which @@ -325,6 +335,12 @@ impl<'a> Drop for TestContext<'a> { let _ = lchflags(entry.path(), FileFlag::empty()); } } + + // Shut down any jails + #[cfg(target_os = "freebsd")] + if let Some(jail) = self.jail.take() { + let _ = jail.kill(); + } } } } diff --git a/rust/src/tests/chflags.rs b/rust/src/tests/chflags.rs index a7606fe8..1adae23e 100644 --- a/rust/src/tests/chflags.rs +++ b/rust/src/tests/chflags.rs @@ -236,5 +236,43 @@ enoent_comp_test_case!(chflags(~path, FileFlag::empty())); // chflags/06.t eloop_comp_test_case!(chflags(~path, FileFlag::empty())); +// chflags/09.t +#[cfg(target_os = "freebsd")] +crate::test_case! { + /// chflags returns EPERM when one of SF_IMMUTABLE, SF_APPEND, or SF_NOUNLINK is set and + /// securelevel is greater than 0 + securelevel, root, FileSystemFeature::Chflags => + [Regular, Dir, Fifo, Block, Char, Socket, Symlink(None)] +} +#[cfg(target_os = "freebsd")] +fn securelevel(ctx: &mut TestContext, ft: FileType) { + use jail::process::Jailed; + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + let jail = jail::StoppedJail::new("/") + .name("pjdfstest_chflags_securelevel") + .param("allow.chflags", jail::param::Value::Int(1)) + .param("securelevel", jail::param::Value::Int(1)); + let jail = jail.start().unwrap(); + ctx.set_jail(jail); + + for flag in [FileFlags::SF_IMMUTABLE, FileFlags::SF_APPEND, FileFlags::SF_NOUNLINK] { + let file = ctx.create(ft.clone()).unwrap(); + lchflags(&file, flag.into()).unwrap(); + + // Since this is a multithreaded application, we can't simply fork and chflags(). Instead, + // execute a child process to test the operation. + let r = std::process::Command::new("/bin/chflags") + .args(["-h", "0"]) + .arg(ctx.base_path().join(&file)) + .jail(&jail) + .output() + .unwrap(); + assert!(!r.status.success()); + assert!(OsStr::from_bytes(&r.stderr).to_string_lossy().contains("Operation not permitted")); + } +} + // chflags/13.t efault_path_test_case!(chflags, |ptr| nix::libc::chflags(ptr, 0));