Skip to content

Commit

Permalink
Don't create directories before we've verified they're inside the tar…
Browse files Browse the repository at this point in the history
… destination (#259)

Fixes #238

Co-authored-by: Robert O'Callahan <robert@ocallahan.org>
  • Loading branch information
benesch and rocallahan authored Aug 10, 2021
1 parent 860858b commit d90a02f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 6 deletions.
27 changes: 22 additions & 5 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,11 +413,8 @@ impl<'a> EntryFields<'a> {
None => return Ok(false),
};

if parent.symlink_metadata().is_err() {
fs::create_dir_all(&parent).map_err(|e| {
TarError::new(&format!("failed to create `{}`", parent.display()), e)
})?;
}
self.ensure_dir_created(&dst, parent)
.map_err(|e| TarError::new(&format!("failed to create `{}`", parent.display()), e))?;

let canon_target = self.validate_inside_dst(&dst, parent)?;

Expand Down Expand Up @@ -761,6 +758,26 @@ impl<'a> EntryFields<'a> {
}
}

fn ensure_dir_created(&self, dst: &Path, dir: &Path) -> io::Result<()> {
let mut ancestor = dir;
let mut dirs_to_create = Vec::new();
while ancestor.symlink_metadata().is_err() {
dirs_to_create.push(ancestor);
if let Some(parent) = ancestor.parent() {
ancestor = parent;
} else {
break;
}
}
for ancestor in dirs_to_create.into_iter().rev() {
if let Some(parent) = ancestor.parent() {
self.validate_inside_dst(dst, parent)?;
}
fs::create_dir(ancestor)?;
}
Ok(())
}

fn validate_inside_dst(&self, dst: &Path, file_dst: &Path) -> io::Result<PathBuf> {
// Abort if target (canonical) parent is outside of `dst`
let canon_parent = file_dst.canonicalize().map_err(|err| {
Expand Down
32 changes: 31 additions & 1 deletion tests/entry.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
extern crate tar;
extern crate tempfile;

use std::fs::File;
use std::fs::{create_dir, File};
use std::io::Read;

use tempfile::Builder;
Expand Down Expand Up @@ -219,6 +219,36 @@ fn modify_link_just_created() {
t!(File::open(td.path().join("foo/bar")));
}

#[test]
#[cfg(not(windows))] // dangling symlinks have weird permissions
fn modify_outside_with_relative_symlink() {
let mut ar = tar::Builder::new(Vec::new());

let mut header = tar::Header::new_gnu();
header.set_size(0);
header.set_entry_type(tar::EntryType::Symlink);
t!(header.set_path("symlink"));
t!(header.set_link_name(".."));
header.set_cksum();
t!(ar.append(&header, &[][..]));

let mut header = tar::Header::new_gnu();
header.set_size(0);
header.set_entry_type(tar::EntryType::Regular);
t!(header.set_path("symlink/foo/bar"));
header.set_cksum();
t!(ar.append(&header, &[][..]));

let bytes = t!(ar.into_inner());
let mut ar = tar::Archive::new(&bytes[..]);

let td = t!(Builder::new().prefix("tar").tempdir());
let tar_dir = td.path().join("tar");
create_dir(&tar_dir).unwrap();
assert!(ar.unpack(tar_dir).is_err());
assert!(!td.path().join("foo").exists());
}

#[test]
fn parent_paths_error() {
let mut ar = tar::Builder::new(Vec::new());
Expand Down
4 changes: 4 additions & 0 deletions tests/header/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ fn set_path() {
assert!(h.set_path(&medium2).is_err());
assert!(h.set_path("\0").is_err());

assert!(h.set_path("..").is_err());
assert!(h.set_path("foo/..").is_err());
assert!(h.set_path("foo/../bar").is_err());

h = Header::new_ustar();
t!(h.set_path("foo"));
assert_eq!(t!(h.path()).to_str(), Some("foo"));
Expand Down

0 comments on commit d90a02f

Please # to comment.