From fb297b9e5fdbbcb61ca985b0ad4c8282455fa047 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 | 129 ++++++++++++++++++++++++++++++++++++-- rust/Cargo.toml | 3 + rust/src/context.rs | 16 +++++ rust/src/tests/chflags.rs | 38 +++++++++++ 4 files changed, 180 insertions(+), 6 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 1c17b81d..d42f54b9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -45,6 +45,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc 0.2.137", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -137,6 +146,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" @@ -162,6 +180,24 @@ dependencies = [ "ghost", ] +[[package]] +name = "jail" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36953efd4638596969dd872a4266e025bc61dafea39ab5531aa90564dda8ab18" +dependencies = [ + "bitflags", + "byteorder", + "libc 0.2.137", + "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.135" @@ -191,6 +227,19 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc 0.2.137", + "memoffset", +] + [[package]] name = "nix" version = "0.25.0" @@ -203,6 +252,12 @@ dependencies = [ "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" @@ -231,14 +286,15 @@ dependencies = [ "figment", "gumdrop", "inventory", - "nix", + "jail", + "nix 0.25.0", "once_cell", "paste", "rand", "serde", - "strum", - "strum_macros", - "sysctl", + "strum 0.24.1", + "strum_macros 0.24.3", + "sysctl 0.5.2", "tempfile", "walkdir", ] @@ -297,6 +353,20 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rctl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6bfd2709ced4ee4c712acb5aa08dc3580c78529434d4c40e9687127247a32a6" +dependencies = [ + "libc 0.2.137", + "nix 0.22.3", + "number_prefix", + "sysctl 0.4.6", + "thiserror", + "users", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -356,13 +426,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]] @@ -371,7 +459,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", @@ -389,6 +477,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", + "byteorder", + "libc 0.2.137", + "thiserror", + "walkdir", +] + [[package]] name = "sysctl" version = "0.5.2" @@ -460,6 +561,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 0.2.137", + "log", +] + [[package]] name = "uuid" version = "1.2.1" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2c57514c..59e13eb1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -24,6 +24,9 @@ rev = "20df092bd067908fba23e49120eed7ad62f29108" git = "https://github.com/nix-rust/nix.git" features = ["fs", "socket", "mount"] +[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));