Re: dscverify, program to check PGP/MD5 from .dsc file
On 09 Dec 1998 01:52:01 -0500, Adam Di Carlo <apharris@burrito.onshore.com> said:
>
> You say:
>
> grep '^File has signature.' $stderr >/dev/null || {
> warn "no signature in $file"
> continue
> }
>
> However, my PGP output doesn't emit that. Mine says:
>
> Good signature from user "Adam Di Carlo <aph@debian.org>".
> Signature made 1998/12/08 15:25 GMT using 1024-bit key, key ID FD5A67CD
The next test in the script checks for the "Good signature from" line.
PGP outputs both:
File has signature. Public key is required to check signature.
.
Good signature from user "Roderick Schertler <roderick@argon.org>".
Signature made 1998/10/08 13:52 GMT using 1024-bit key, key ID CB7E2209
> What would be really cool is if dinstall pgp validation bit could be
> broken out into its own script.
I've just looked at what dinstall uses. It uses PGP's return code! I'd
tested this and found that PGP didn't return a meaningful exit code (it
exited 0 even if the signature check failed). It turns out that it only
behaves rationally for this if you use the +batchmode switch! PGP is so
disgusting. I look forward to ditching it.
Here is a modified version which uses +batchmode, like dinstall does.
It requires the MD5 Perl module now, which is in libmd5-perl.
#!/usr/bin/perl -w
use strict;
# $Id: dscverify,v 1.5 1998-12-09 12:10:11-05 roderick Exp $
#
# Roderick Schertler <roderick@argon.org>
# This program takes .changes or .dsc files as arguments and verifies
# that they're properly signed by a Debian developer, and that the local
# copies of the files mentioned in them match the MD5 sums given.
# Copyright (C) 1998 Roderick Schertler
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# For a copy of the GNU General Public License write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
use 5.004; # correct pipe close behavior
use POSIX qw(:errno_h);
use MD5 ();
(my $Me = $0) =~ s-.*/--;
my $Exit = 0;
sub xwarndie_mess {
my @mess = ("$Me: ", @_);
$mess[$#mess] =~ s/:$/: $!\n/; # XXX loses if it's really /:\n/
return @mess;
}
sub xwarn {
warn xwarndie_mess @_;
$Exit ||= 1;
}
sub xdie {
die xwarndie_mess @_;
}
sub get_ring {
for (qw(/debian/home/maor/dinstall/debian-keyring.pgp
/usr/share/keyrings/debian-keyring.pgp)) {
return $_ if -r;
}
xdie "can't find debian-keyring.pgp\n";
}
sub process_file {
my ($ring, $file) = @_;
my ($any, $output, @spec);
print "$file:\n";
unless (open STDIN, $file) {
xwarn "can't read $file:";
return;
}
# NB: If you don't use +batchmode pgp doesn't use the exit status
# to indicate whether the signature was good.
open PGP, "pgp +pubring=\Q$ring\E +batchmode -f 2>/dev/null |"
or xdie "can't fork:";
# I read and save the output from pgp before checking anything so
# that I don't misleadingly "validate" any files from a source which
# fails its signature check.
{
local $/;
$output = <PGP>;
}
unless (close PGP) {
xwarn $! ? "error closing pgp:" : "$file failed signature check\n";
return;
}
@spec = map { split /\n/ } $output =~ /^Files:\s*\n((?:[\040\t]+.*\n)+)/mg;
unless (@spec) {
xwarn "no file spec lines in $file\n";
return;
}
my $md5o = MD5->new or xdie "can't initialize MD5\n";
for (@spec) {
unless (/^\s+(\S+)\s+(\d+)\s+(?:\S+\s+\S+\s+)?(\S+)\s*$/) {
xwarn "invalid file spec in $file `$_'\n";
next;
}
my ($md5, $size, $file) = ($1, $2, $3);
unless (open FILE, $file) {
if ($! == ENOENT) {
print " skipping $file\n";
}
else {
xwarn "can't read $file:";
}
next;
}
$any = 1;
print " validating $file\n";
# size
my $this_size = -s FILE;
unless (defined $this_size) {
xwarn "can't fstat $file:";
next;
}
unless ($this_size == $size) {
xwarn "invalid file length for $file (wanted $size got $this_size)\n";
next;
}
# MD5
$md5o->reset;
$md5o->addfile(*FILE);
my $this_md5 = $md5o->hexdigest;
unless ($this_md5 eq $md5) {
xwarn "MD5 mismatch for $file (wanted $md5 got $this_md5)\n";
next;
}
}
$any or xwarn "$file didn't specify any files present locally\n";
}
sub main {
my $ring = get_ring;
@ARGV or xdie "no .changes or .dsc files specified\n";
for my $file (@ARGV) {
process_file $ring, $file;
}
return 0;
}
$Exit = main || $Exit;
$Exit = 1 if $Exit and not $Exit % 256;
exit $Exit;
--
Roderick Schertler
roderick@argon.org
Reply to: