Canonicalize blacklist overlay paths to skirt /var/run symlink
This commit is contained in:
+14
-10
@@ -14,8 +14,8 @@ pub struct BlacklistOverlays {
|
||||
}
|
||||
|
||||
pub fn resolve_overlays(ctx: &PathContext) -> Result<BlacklistOverlays, SandboxError> {
|
||||
let mut tmpfs_dirs = Vec::new();
|
||||
let mut null_bind_files = Vec::new();
|
||||
let mut tmpfs_dirs: Vec<PathBuf> = Vec::new();
|
||||
let mut null_bind_files: Vec<PathBuf> = Vec::new();
|
||||
|
||||
for raw in SENSITIVE_PATHS {
|
||||
let expanded = expand_path(raw, ctx);
|
||||
@@ -23,9 +23,13 @@ pub fn resolve_overlays(ctx: &PathContext) -> Result<BlacklistOverlays, SandboxE
|
||||
paths.sort_by_key(|p| !p.is_dir());
|
||||
for path in paths {
|
||||
match classify_path(&path) {
|
||||
PathKind::Dir => tmpfs_dirs.push(path),
|
||||
PathKind::File => {
|
||||
if !is_under_tmpfs_dir(&path, &tmpfs_dirs) {
|
||||
PathKind::Dir(path) => {
|
||||
if !tmpfs_dirs.contains(&path) {
|
||||
tmpfs_dirs.push(path);
|
||||
}
|
||||
}
|
||||
PathKind::File(path) => {
|
||||
if !is_under_tmpfs_dir(&path, &tmpfs_dirs) && !null_bind_files.contains(&path) {
|
||||
null_bind_files.push(path);
|
||||
}
|
||||
}
|
||||
@@ -47,15 +51,15 @@ pub fn resolve_path_context() -> Result<PathContext, SandboxError> {
|
||||
}
|
||||
|
||||
enum PathKind {
|
||||
Dir,
|
||||
File,
|
||||
Dir(PathBuf),
|
||||
File(PathBuf),
|
||||
Missing,
|
||||
}
|
||||
|
||||
fn classify_path(path: &Path) -> PathKind {
|
||||
match fs::metadata(path) {
|
||||
Ok(m) if m.is_dir() => PathKind::Dir,
|
||||
Ok(_) => PathKind::File,
|
||||
match fs::canonicalize(path) {
|
||||
Ok(path) if path.is_dir() => PathKind::Dir(path),
|
||||
Ok(path) => PathKind::File(path),
|
||||
Err(_) => PathKind::Missing,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1393,6 +1393,47 @@ fn seccomp_bash_pthread_fallback_works() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_overlays_survive_absolute_var_run_symlink() {
|
||||
// On Debian/Ubuntu, /var/run -> /run is an absolute symlink; overlays
|
||||
// like --tmpfs /var/run/dbus trip bwrap's re-rooted symlink resolution.
|
||||
// Arch ships /var/run -> ../run (relative) so we synthesize the absolute
|
||||
// layout inside the sandbox to reproduce on any host.
|
||||
let mut bwrap_args = build_bwrap_command(&["--no-seccomp", "--", "true"]);
|
||||
inject_absolute_var_run_symlink(&mut bwrap_args);
|
||||
|
||||
let output = Command::new(&bwrap_args[0])
|
||||
.args(&bwrap_args[1..])
|
||||
.output()
|
||||
.expect("failed to invoke bwrap directly");
|
||||
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"bwrap failed — an overlay target traverses an absolute /var/run symlink.\n\
|
||||
stderr: {}",
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
}
|
||||
|
||||
fn build_bwrap_command(sandbox_args: &[&str]) -> Vec<String> {
|
||||
let output = sandbox(&["--dry-run"])
|
||||
.args(sandbox_args)
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
let cmd = String::from_utf8_lossy(&output.stdout);
|
||||
let parsed = shlex::split(cmd.trim()).expect("dry-run output is not valid shell");
|
||||
assert_eq!(parsed[0], "bwrap");
|
||||
parsed
|
||||
}
|
||||
|
||||
fn inject_absolute_var_run_symlink(bwrap_args: &mut Vec<String>) {
|
||||
assert_eq!(bwrap_args[1], "--ro-bind");
|
||||
assert_eq!(bwrap_args[2], "/");
|
||||
assert_eq!(bwrap_args[3], "/");
|
||||
let flags = ["--tmpfs", "/var", "--symlink", "/run", "/var/run"].map(String::from);
|
||||
bwrap_args.splice(4..4, flags);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seccomp_blocks_tiocsti() {
|
||||
// TIOCSTI (0x5412) injects keystrokes into the terminal input queue.
|
||||
|
||||
Reference in New Issue
Block a user