From 4dedcc271983fca70cf406b6913a6a6626747ed7 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 27 Oct 2023 01:22:10 +0200 Subject: [PATCH] Fix intermediate symlink directories --- Cargo.toml | 5 +++++ src/linux/namespaces.rs | 22 ++++++++++++++-------- tests/exec_symlinked.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 tests/exec_symlinked.rs diff --git a/Cargo.toml b/Cargo.toml index b0cffa4..dab1b70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,11 @@ name = "exec" path = "tests/exec.rs" harness = false +[[test]] +name = "exec_symlinked" +path = "tests/exec_symlinked.rs" +harness = false + [[test]] name = "fs" path = "tests/fs.rs" diff --git a/src/linux/namespaces.rs b/src/linux/namespaces.rs index daf996d..0e7c734 100644 --- a/src/linux/namespaces.rs +++ b/src/linux/namespaces.rs @@ -74,15 +74,23 @@ fn create_mount_namespace(bind_mounts: HashMap) -> Resu let mut symlinks = Vec::new(); let mut bind_mounts = bind_mounts .into_iter() - .filter_map(|(path, exception)| { - let canonicalized = path.canonicalize().ok()?; + .filter_map(|(path, exception)| match path.read_link() { + // Handle paths where final component is a symbolic link. + Ok(target) => { + let canonicalized = path.canonicalize().ok()?; - // Store original symlink path to create it if necessary. - if let Ok(target) = path.read_link() { + // Store original symlink path to create it if necessary. symlinks.push((path, target)); - } - Some((canonicalized, exception)) + Some((canonicalized, exception)) + }, + // Handle paths where final component is a file or directory. + Err(_) => { + // Ensure path is absolute, without following symlinks. + let absolute = absolute(&path).ok()?; + let normalized = normalize_path(&absolute); + Some((normalized, exception)) + }, }) .collect::>(); @@ -194,8 +202,6 @@ fn copy_tree(src: impl AsRef, dst: impl AsRef) -> Result<()> { src_sub = src_sub.join(component); dst = dst.join(component); - // TODO: symlink_metadata().is_ok()? - // // Skip nodes that already exist. if dst.exists() { continue; diff --git a/tests/exec_symlinked.rs b/tests/exec_symlinked.rs new file mode 100644 index 0000000..305228d --- /dev/null +++ b/tests/exec_symlinked.rs @@ -0,0 +1,41 @@ +use std::fs; +use std::os::unix::fs as unixfs; +use std::path::PathBuf; +use std::process::Command; + +use birdcage::{Birdcage, Exception, Sandbox}; + +fn main() { + // Create symlinked executable. + let tempdir = tempfile::tempdir().unwrap().into_path(); + let exec_dir = tempdir.join("bin"); + fs::create_dir(&exec_dir).unwrap(); + let symlink_exec = exec_dir.join("true"); + unixfs::symlink("/usr/bin/true", &symlink_exec).unwrap(); + + // Create symlinked dir with non-symlinked executable. + let truer_path = exec_dir.join("truer"); + fs::copy("/usr/bin/true", &truer_path).unwrap(); + let symlink_dir = tempdir.join("symbin"); + let symlink_dir_exec = symlink_dir.join("truer"); + unixfs::symlink(&exec_dir, &symlink_dir).unwrap(); + + let mut birdcage = Birdcage::new(); + birdcage.add_exception(Exception::ExecuteAndRead(symlink_dir_exec.clone())).unwrap(); + birdcage.add_exception(Exception::ExecuteAndRead(symlink_exec.clone())).unwrap(); + if PathBuf::from("/lib64").exists() { + birdcage.add_exception(Exception::ExecuteAndRead("/lib64".into())).unwrap(); + } + if PathBuf::from("/lib").exists() { + birdcage.add_exception(Exception::ExecuteAndRead("/lib".into())).unwrap(); + } + birdcage.lock().unwrap(); + + // Ensure symlinked executable works. + let cmd = Command::new(symlink_exec).status().unwrap(); + assert!(cmd.success()); + + // Ensure symlinked dir's executable works. + let cmd = Command::new(symlink_dir_exec).status().unwrap(); + assert!(cmd.success()); +}