[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: