[Reproducible-commits] [dpkg] 15/17: Dpkg::Source::Patch: Correctly parse C-style diff filenames
Holger Levsen
holger at layer-acht.org
Tue May 3 08:43:43 UTC 2016
This is an automated email from the git hooks/post-receive script.
holger pushed a commit to annotated tag 1.16.13
in repository dpkg.
commit 73203c0bc4af425300fdcddc4be72624855798e7
Author: Guillem Jover <guillem at debian.org>
Date: Tue Apr 15 08:15:44 2014 +0200
Dpkg::Source::Patch: Correctly parse C-style diff filenames
We need to strip the surrounding quotes, and unescape any escape
sequence, so that we check the same files that the patch program will
be using, otherwise a malicious package could overpass those checks,
and perform directory traversal attacks on source package unpacking.
Fixes: CVE-2014-0471
Reported-by: Jakub Wilk <jwilk at debian.org>
---
debian/changelog | 3 +++
scripts/Dpkg/Source/Patch.pm | 62 +++++++++++++++++++++++++++++++++++++-------
2 files changed, 55 insertions(+), 10 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index 397c0f5..975d955 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -20,6 +20,9 @@ dpkg (1.16.13) UNRELEASED; urgency=low
setting package selections. Suggested by Klaus Ita <koki.eml at gmail.com>.
* Recognize «start-stop-daemon -C» as documented. Closes: #719746
Reported by Brian S. Julin <bri at abrij.org>.
+ * Correctly parse C-style diff filenames in Dpkg::Source::Patch, to avoid
+ directory traversal attempts from hostile source packages when unpacking
+ them. Reported by Jakub Wilk <jwilk at debian.org>. Fixes CVE-2014-0471.
[ Updated scripts translations ]
* Fix a typo in the German scripts translation.
diff --git a/scripts/Dpkg/Source/Patch.pm b/scripts/Dpkg/Source/Patch.pm
index da4f7c2..d9b084e 100644
--- a/scripts/Dpkg/Source/Patch.pm
+++ b/scripts/Dpkg/Source/Patch.pm
@@ -310,6 +310,56 @@ sub _fail_not_same_type {
$self->register_error();
}
+my %ESCAPE = ((
+ 'a' => "\a",
+ 'b' => "\b",
+ 'f' => "\f",
+ 'n' => "\n",
+ 'r' => "\r",
+ 't' => "\t",
+ 'v' => "\cK",
+ '\\' => "\\",
+ '"' => "\"",
+), (
+ map { sprintf('%03o', $_) => chr($_) } (0..255)
+));
+
+sub _unescape {
+ my ($diff, $str) = @_;
+
+ if (exists $ESCAPE{$str}) {
+ return $ESCAPE{$str};
+ } else {
+ error(_g("diff %s patches file with unknown escape sequence \\%s"),
+ $diff, $str);
+ }
+}
+
+# Fetch the header filename ignoring the optional timestamp
+sub _fetch_filename {
+ my ($diff, $header) = @_;
+
+ # Strip any leading spaces.
+ $header =~ s/^\s+//;
+
+ # Is it a C-style string?
+ if ($header =~ m/^"/) {
+ $header =~ m/^"((?:[^\\"]|\\.)*)"/;
+ error(_g('diff %s patches file with unbalanced quote'), $diff)
+ unless defined $1;
+
+ $header = $1;
+ $header =~ s/\\([0-3][0-7]{2}|.)/_unescape($diff, $1)/eg;
+ } else {
+ # Tab is the official separator, it's always used when
+ # filename contain spaces. Try it first, otherwise strip on space
+ # if there's no tab
+ $header =~ s/\s.*// unless $header =~ s/\t.*//;
+ }
+
+ return $header;
+}
+
# check diff for sanity, find directories to create as a side effect
sub analyze {
my ($self, $destdir, %opts) = @_;
@@ -332,14 +382,6 @@ sub analyze {
}
return $line;
}
- sub strip_ts { # Strip timestamp
- my $header = shift;
- # Tab is the official separator, it's always used when
- # filename contain spaces. Try it first, otherwise strip on space
- # if there's no tab
- $header =~ s/\s.*// unless ($header =~ s/\t.*//);
- return $header;
- }
sub intuit_file_patched {
my ($old, $new) = @_;
return $new unless defined $old;
@@ -389,7 +431,7 @@ sub analyze {
unless(s/^--- //) {
error(_g("expected ^--- in line %d of diff `%s'"), $., $diff);
}
- $path{'old'} = $_ = strip_ts($_);
+ $path{'old'} = $_ = _fetch_filename($diff, $_);
$fn{'old'} = $_ if $_ ne '/dev/null' and s{^[^/]*/+}{$destdir/};
if (/\.dpkg-orig$/) {
error(_g("diff `%s' patches file with name ending .dpkg-orig"), $diff);
@@ -401,7 +443,7 @@ sub analyze {
unless (s/^\+\+\+ //) {
error(_g("line after --- isn't as expected in diff `%s' (line %d)"), $diff, $.);
}
- $path{'new'} = $_ = strip_ts($_);
+ $path{'new'} = $_ = _fetch_filename($diff, $_);
$fn{'new'} = $_ if $_ ne '/dev/null' and s{^[^/]*/+}{$destdir/};
unless (defined $fn{'old'} or defined $fn{'new'}) {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/dpkg.git
More information about the Reproducible-commits
mailing list