dpkg: Security Advisory - CVE-2014-0471 commit a82651188476841d190c58693f95827d61959b51 upstream Directory traversal vulnerability in the unpacking functionality in dpkg before 1.15.9, 1.16.x before 1.16.13, and 1.17.x before 1.17.8 allows remote attackers to write arbitrary files via a crafted source package, related to "C-style filename quoting." Upstream-Status: Backport Signed-off-by: Wenlin Kang Signed-off-by: Wenzong Fan =================================================== diff -uarN dpkg-1.17.1-org/scripts/Dpkg/Source/Patch.pm dpkg-1.17.1/scripts/Dpkg/Source/Patch.pm --- dpkg-1.17.1-org/scripts/Dpkg/Source/Patch.pm 2014-06-05 15:24:07.422446284 +0800 +++ dpkg-1.17.1/scripts/Dpkg/Source/Patch.pm 2014-06-05 15:41:37.746446314 +0800 @@ -324,14 +324,53 @@ return $line; } -# Strip timestamp -sub _strip_ts { - 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.*//); +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; } @@ -400,7 +439,7 @@ 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); @@ -412,7 +451,7 @@ 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}) {