[SCM] Debian package checker branch, wheezy, updated. 2.5.10.3-5-g54bac3d
The following commit has been merged in the wheezy branch:
commit a167f638c6c371020c11fecfd8496c53268e75a9
Author: Niels Thykier <niels@thykier.net>
Date: Tue Dec 18 21:04:21 2012 +0100
L::Util: Be very strict around PGP markers
Be very strict in parsing and handling of PGP headers. Particularly,
reject any file with a "suspicious" use of PGP messages (like multiple
signed messages).
Signed-off-by: Niels Thykier <niels@thykier.net>
diff --git a/debian/changelog b/debian/changelog
index 441b2e1..0ec1fc1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -8,6 +8,10 @@ lintian (2.5.10.4) unstable; urgency=low
* lib/Lintian/Collect/Package.pm:
+ [NT] Ensure the "root" entry of indices do not contain itself.
(Closes: #695866)
+ * lib/Lintian/Util.pm:
+ + [NT] Reject partially signed Deb822 files. Most Deb822 files
+ are not signed at all; but those that are should be completely
+ covered by a signature. (Closes: #696230)
-- Niels Thykier <niels@thykier.net> Sat, 13 Feb 2013 12:50:00 +0100
diff --git a/frontend/lintian b/frontend/lintian
index 7a9030c..d3a9912 100755
--- a/frontend/lintian
+++ b/frontend/lintian
@@ -956,7 +956,13 @@ while (my $arg = shift) {
# file?
if (-f $arg) {
if ($arg =~ m/\.(?:u?deb|dsc|changes)$/o){
- $pool->add_file($arg);
+ eval {
+ $pool->add_file($arg);
+ };
+ if ($@) {
+ print STDERR "Skipping $arg: $@";
+ $exit_code = 2;
+ }
} else {
fail("bad package file name $arg (neither .deb, .udeb, .changes or .dsc file)");
}
diff --git a/lib/Lintian/Util.pm b/lib/Lintian/Util.pm
index 38c385d..0008cfa 100644
--- a/lib/Lintian/Util.pm
+++ b/lib/Lintian/Util.pm
@@ -227,8 +227,14 @@ refer to L</CONSTANTS> for the list of constants and their meaning.
The default value for FLAGS is 0.
If the file is empty (i.e. it contains no paragraphs), the method will
-contain an I<empty> list. Lines looking like a GPG-signature is
-ignored when parsing the file.
+contain an I<empty> list. The deb822 contents may be inside a
+I<signed> PGP message with a signature.
+
+visit_dpkg_paragraph will require the PGP headers to be correct (if
+present) and require that the entire file is covered by the signature.
+However, it will I<not> validate the signature (in fact, the contents
+of the PGP SIGNATURE part can be empty). The signature should be
+validated separatedly.
visit_dpkg_paragraph will pass paragraphs to CODE as they are
completed. If CODE can process the paragraphs as they are seen, very
@@ -295,6 +301,45 @@ underscores.
A comment line appeared and FLAGS contained DCTRL_NO_COMMENTS.
+=item PGP signature seen before start of signed message
+
+A "BEGIN PGP SIGNATURE" header is seen and a "BEGIN PGP MESSAGE" has
+not been seen yet.
+
+=item Two PGP signatures (first one at line %d)
+
+Two "BEGIN PGP SIGNATURE" headers are seen in the same file.
+
+=item Unexpected %s header
+
+A valid PGP header appears (e.g. "BEGIN PUBLIC KEY BLOCK").
+
+=item Malformed PGP header
+
+An invalid or malformed PGP header appears.
+
+=item Expected at most one signed message (previous at line %d)
+
+Two "BEGIN PGP MESSAGE" headers appears in the same message.
+
+=item End of file but expected a "END PGP SIGNATURE" header
+
+The file ended after a "BEGIN PGP SIGNATURE" header without being
+followed by a "END PGP SIGNATURE".
+
+=item PGP MESSAGE header must be first content if present
+
+The file had content before PGP MESSAGE.
+
+=item Data after the PGP SIGNATURE
+
+The file had data after the PGP SIGNATURE block ended.
+
+=item End of file before "BEGIN PGP SIGNATURE"
+
+The file had a "BEGIN PGP MESSAGE" header, but no signature was
+present.
+
=back
=cut
@@ -307,13 +352,13 @@ sub visit_dpkg_paragraph {
my $open_section = 0;
my $last_tag;
my $debconf = $flags & DCTRL_DEBCONF_TEMPLATE;
+ my $signed = 0;
+ my $signature = 0;
local $_;
while (<$CONTROL>) {
chomp;
- # FIXME: comment lines are only allowed in debian/control and should
- # be an error for other control files.
if (/^\#/) {
next unless $flags & DCTRL_NO_COMMENTS;
die "syntax error at line $.: Comments are not allowed.\n";
@@ -328,18 +373,101 @@ sub visit_dpkg_paragraph {
$open_section = 0;
}
}
- # pgp sig?
- elsif (m/^-----BEGIN PGP SIGNATURE/) { # skip until end of signature
+ # pgp sig? Be strict here (due to #696230)
+ # According to http://tools.ietf.org/html/rfc4880#section-6.2
+ # The header MUST start at the beginning of the line and MUST NOT have
+ # any other text (except whitespace) after the header.
+ elsif (m/^-----BEGIN PGP SIGNATURE-----\s*$/) { # skip until end of signature
+ my $saw_end = 0;
+ if (not $signed or $signature) {
+ die "syntax error at line $.: PGP signature seen before start of signed message\n"
+ if not $signed;
+ die "syntax error at line $.: Two PGP signatures (first one at line $signature)\n";
+ }
+ $signature = $.;
while (<$CONTROL>) {
- last if m/^-----END PGP SIGNATURE/o;
+ if (m/^-----END PGP SIGNATURE-----\s*$/o) {
+ $saw_end = 1;
+ last;
+ }
}
+ # The "at line X" may seem a little weird, but it keeps the
+ # message format identical.
+ die "syntax error at line $.: End of file but expected a \"END PGP SIGNATURE\" header\n"
+ unless $saw_end;
}
# other pgp control?
- elsif (m/^-----BEGIN PGP/) { # skip until the next blank line
+ elsif (m/^-----(?:BEGIN|END) PGP/) {
+ # At this point it could be a malformed PGP header or one
+ # of the following valid headers (RFC4880):
+ # * BEGIN PGP MESSAGE
+ # - Possibly a signed Debian CTRL, so okay (for now)
+ # * BEGIN PGP {PUBLIC,PRIVATE} KEY BLOCK
+ # - Valid header, but not a Debian CTRL file.
+ # * BEGIN PGP MESSAGE, PART X{,/Y}
+ # - Valid, but we don't support partial messages, so
+ # bail on those.
+
+ unless (m/^-----BEGIN PGP SIGNED MESSAGE-----\s*$/) {
+ # Not a (full) PGP MESSAGE; reject.
+
+ my $key = qr/(?:BEGIN|END) PGP (?:PUBLIC|PRIVATE) KEY BLOCK/;
+ my $msgpart = qr{BEGIN PGP MESSAGE, PART \d+(?:/\d+)?};
+ my $msg = qr/(?:BEGIN|END) PGP (?:(?:COMPRESSED|ENCRYTPED) )?MESSAGE/;
+
+ if (m/^-----($key|$msgpart|$msg)-----\s*$/o) {
+ die "syntax error at line $.: Unexpected $1 header\n";
+ } else {
+ die "syntax error at line $.: Malformed PGP header\n";
+ }
+ } else {
+ if ($signed) {
+ die "syntax error at line $.: Expected at most one signed message" .
+ " (previous at line $signed)\n"
+ }
+ if ($sline > -1) {
+ # NB: If you remove this, keep in mind that it may allow two paragraphs to
+ # merge. Consider:
+ #
+ # Field-P1: some-value
+ # -----BEGIN PGP SIGANTURE----
+ #
+ # Field-P2: another value
+ #
+ # At the time of writing: If $open_section is
+ # true, it will remain so until the empty line
+ # after the PGP header.
+ die "syntax error at line $.: PGP MESSAGE header must be first" .
+ " content if present\n";
+ }
+ $signed = $.;
+ }
+
+ # skip until the next blank line
while (<$CONTROL>) {
last if /^\s*$/o;
}
}
+ # did we see a signature already? We allow all whitespace/comment lines
+ # outside the signature.
+ elsif ($signature) {
+ # Accept empty lines after the signature.
+ next if m/^\s*$/;
+
+ #NB: If you remove this, keep in mind that it may allow two paragraphs to
+ # merge. Consider:
+ #
+ # Field-P1: some-value
+ # -----BEGIN PGP SIGANTURE----
+ # [...]
+ # -----END PGP SIGANTURE----
+ # Field-P2: another value
+ #
+ # At the time of writing: If $open_section is
+ # true, it will remain so until the empty line
+ # after the PGP header.
+ die "syntax error at line $.: Data after the PGP SIGNATURE\n";
+ }
# new empty field?
elsif (m/^([^: \t]+):\s*$/o) {
$sline = $. if not $open_section;
@@ -395,6 +523,14 @@ sub visit_dpkg_paragraph {
}
# pass the last section (if not already done).
$code->($section, $sline) if $open_section;
+
+ # Given the API, we cannot use this check to prevent any paragraphs from being
+ # emitted to the code argument, so we might as well just do this last.
+ if ($signed and not $signature) {
+ # The "at line X" may seem a little weird, but it keeps the
+ # message format identical.
+ die "syntax error at line $.: End of file before \"BEGIN PGP SIGNATURE\"\n";
+ }
}
=item read_dpkg_control (FILE[, FLAGS[, LINES]])
diff --git a/t/scripts/Lintian/Util/data/pgp-eof-missing-sign b/t/scripts/Lintian/Util/data/pgp-eof-missing-sign
new file mode 100644
index 0000000..78e5ee9
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-eof-missing-sign
@@ -0,0 +1,5 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: lintian
+
+# Missing signature block
diff --git a/t/scripts/Lintian/Util/data/pgp-leading-unsigned b/t/scripts/Lintian/Util/data/pgp-leading-unsigned
new file mode 100644
index 0000000..0b6b949
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-leading-unsigned
@@ -0,0 +1,14 @@
+Package: dpkg
+
+# Unsigned above, signed below (bad)
+
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: lintian
+
+-----BEGIN PGP SIGNATURE-----
+
+Some signature.
+
+-----END PGP SIGNATURE-----
+
diff --git a/t/scripts/Lintian/Util/data/pgp-malformed-header b/t/scripts/Lintian/Util/data/pgp-malformed-header
new file mode 100644
index 0000000..f83fb77
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-malformed-header
@@ -0,0 +1,11 @@
+# Missing a dash in the end
+
+-----BEGIN PGP SIGNED MESSAGE----
+
+Package: linitan
+
+-----BEGIN PGP SIGNATURE----
+
+Some signature.
+
+-----END PGP SIGNATURE----
diff --git a/t/scripts/Lintian/Util/data/pgp-no-end-pgp-header b/t/scripts/Lintian/Util/data/pgp-no-end-pgp-header
new file mode 100644
index 0000000..6d15d98
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-no-end-pgp-header
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: lintian
+
+-----BEGIN PGP SIGNATURE-----
+
+Some signature, missing an end marker.
diff --git a/t/scripts/Lintian/Util/data/pgp-sig-before-start b/t/scripts/Lintian/Util/data/pgp-sig-before-start
new file mode 100644
index 0000000..1d7cf12
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-sig-before-start
@@ -0,0 +1,7 @@
+Package: lintian
+
+-----BEGIN PGP SIGNATURE-----
+
+Some signature.
+
+-----END PGP SIGNATURE-----
diff --git a/t/scripts/Lintian/Util/data/pgp-trailing-unsigned b/t/scripts/Lintian/Util/data/pgp-trailing-unsigned
new file mode 100644
index 0000000..1d29d87
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-trailing-unsigned
@@ -0,0 +1,14 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: lintian
+
+-----BEGIN PGP SIGNATURE-----
+
+Some signature.
+
+-----END PGP SIGNATURE-----
+
+# Signed above, unsigned below (bad)
+
+Package: dpkg
+
diff --git a/t/scripts/Lintian/Util/data/pgp-two-signatures b/t/scripts/Lintian/Util/data/pgp-two-signatures
new file mode 100644
index 0000000..0c0b7d6
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-two-signatures
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: lintian
+
+-----BEGIN PGP SIGNATURE-----
+
+Some signature.
+
+-----END PGP SIGNATURE-----
+
+-----BEGIN PGP SIGNATURE-----
+
+Another signature.
+
+-----END PGP SIGNATURE-----
+
diff --git a/t/scripts/Lintian/Util/data/pgp-two-signed-msgs b/t/scripts/Lintian/Util/data/pgp-two-signed-msgs
new file mode 100644
index 0000000..c8fcf9d
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-two-signed-msgs
@@ -0,0 +1,19 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: lintian
+
+-----BEGIN PGP SIGNATURE-----
+
+Some signature.
+
+-----END PGP SIGNATURE-----
+
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Package: dpkg
+
+-----BEGIN PGP SIGNATURE-----
+
+Aother signature.
+
+-----END PGP SIGNATURE-----
diff --git a/t/scripts/Lintian/Util/data/pgp-unexpected-header b/t/scripts/Lintian/Util/data/pgp-unexpected-header
new file mode 100644
index 0000000..743ac85
--- /dev/null
+++ b/t/scripts/Lintian/Util/data/pgp-unexpected-header
@@ -0,0 +1,6 @@
+-----BEGIN PGP MESSAGE-----
+
+We are expecting a "SIGNED" message.
+
+-----END PGP MESSAGE-----
+
diff --git a/t/scripts/Lintian/Util/dctrl-parser.t b/t/scripts/Lintian/Util/dctrl-parser.t
new file mode 100644
index 0000000..7dd5a5f
--- /dev/null
+++ b/t/scripts/Lintian/Util/dctrl-parser.t
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Lintian::Util qw(visit_dpkg_paragraph);
+
+my $syntax_error = qr/syntax error at line \d+/;
+my %TESTS_BAD = (
+ 'pgp-sig-before-start' => qr/${syntax_error}: PGP signature seen before start of signed message/,
+ 'pgp-two-signatures' => qr/${syntax_error}: Two PGP signatures \(first one at line \d+\)/,
+ 'pgp-unexpected-header' => qr/${syntax_error}: Unexpected .+ header/,
+ 'pgp-malformed-header' => qr/${syntax_error}: Malformed PGP header/,
+ 'pgp-two-signed-msgs' => qr/${syntax_error}: Expected at most one signed message \(previous at line \d+\)/,
+ 'pgp-no-end-pgp-header' => qr/${syntax_error}: End of file but expected a "END PGP SIGNATURE" header/,
+ 'pgp-leading-unsigned' => qr/${syntax_error}: PGP MESSAGE header must be first content if present/,
+ 'pgp-trailing-unsigned' => qr/${syntax_error}: Data after the PGP SIGNATURE/,
+ 'pgp-eof-missing-sign' => qr/${syntax_error}: End of file before "BEGIN PGP SIGNATURE"/,
+);
+
+my $DATADIR = $0;
+$DATADIR =~ s,[^/]+$,,o;
+if ($DATADIR) {
+ # invokved in some other dir
+ $DATADIR = "$DATADIR/data";
+} else {
+ # current dir
+ $DATADIR = 'data';
+}
+
+plan skip_all => 'Data files not available'
+ unless -d $DATADIR;
+
+plan tests => scalar keys %TESTS_BAD;
+
+foreach my $filename (sort keys %TESTS_BAD) {
+ my $fail_regex = $TESTS_BAD{$filename};
+ my $path = "$DATADIR/$filename";
+ open my $fd, '<', $path or die "open $path: $!";
+ eval {
+ visit_dpkg_paragraph (sub {}, $fd);
+ };
+ close $fd;
+ if (my $err = $@) {
+ like ($err, $fail_regex, $filename);
+ } else {
+ fail ("$path was parsed successfully");
+ }
+}
+
--
Debian package checker
Reply to: