[Reproducible-commits] [dpkg] 29/32: Dpkg::Source::Patch: Correctly parse C-style diff filenames

Holger Levsen holger at layer-acht.org
Tue May 3 08:43:15 UTC 2016


This is an automated email from the git hooks/post-receive script.

holger pushed a commit to annotated tag 1.15.10
in repository dpkg.

commit 75b60947d39b2483e6e788c8b6cfe9fbc2623941
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
    
    Cherry picked from commit 73203c0bc4af425300fdcddc4be72624855798e7.
    
    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             |  8 ++++++
 scripts/Dpkg/Source/Patch.pm | 62 +++++++++++++++++++++++++++++++++++++-------
 2 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index c6b6d57..3348289 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+dpkg (1.15.9) UNRELEASED; urgency=low
+
+  * 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.
+
+ -- Guillem Jover <guillem at debian.org>  Wed, 23 Apr 2014 03:02:47 +0200
+
 dpkg (1.15.8.13) stable; urgency=low
 
   [ Guillem Jover ]
diff --git a/scripts/Dpkg/Source/Patch.pm b/scripts/Dpkg/Source/Patch.pm
index 59d1004..55d93b9 100644
--- a/scripts/Dpkg/Source/Patch.pm
+++ b/scripts/Dpkg/Source/Patch.pm
@@ -280,6 +280,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) = @_;
@@ -299,14 +349,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;
@@ -351,7 +393,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);
@@ -363,7 +405,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