pgpcontrol/pgpverify

243 lines
8.5 KiB
Text
Raw Normal View History

#! /usr/bin/perl -ws
# written April 1996, tale@isc.org (David C Lawrence)
# Version 1.10
#
# NOTICE TO INN MAINTAINERS: The version that is shipped with INN
# is the same as the version that I make available to the rest of the
# world (including non-INN sites), so please make all changes through me.
#
# Changes from 1.9 -> 1.10
# -- minor diddling for INN 2.0: use $inn/pathtmp if it exists, and
# work with the new subst method to find innshellvars.pl
# -- do not truncate the tmp file when opening, in case it is really linked
# to another file
#
# Changes from 1.8 -> 1.9
# -- match 'Bad signature' pgp output to return exit status 3 by removing
# '^' in regexp matched on multiline string.
#
# Changes from 1.7 -> 1.8
# -- ignore final dot-CRLF if article is in NNTP format
#
# Changes from 1.6 -> 1.7
# -- parse PGP 5.0 'good signature' lines.
# -- allow -test swtich; prints pgp input and output
# -- look for pgp in INN's innshellvars.pl
# -- changed regexp delimiters for stripping $0 to be compatible with old perl
#
# Changes from 1.5 -> 1.6
# -- handle articles encoded in NNTP format ('.' starting line is doubled,
# \r\n at line end) by stripping NNTP encoding.
# -- exit 255 with pointer to $HOME or $PGPPATH if pgp can't find key ring.
# (probably doesn't match the necessary error message with ViaCrypt PGP)
# -- failures also report message-id so the article can be looked up to retry.
#
# Changes from 1.4 -> 1.5
# -- force English lanugage for 'Good signature from user' by passing
# +language=en on pgp command line, rather than setting the
# environment variable LANGUAGE to 'en'.
#
# Changes from 1.3 -> 1.4
# -- now handles wrapped headers that have been unfolded.
# (though I do believe news software oughtn't be unfolding them.)
# -- checks to ensure that the temporary file is really a file, and
# not a link or some other weirdness
1996-05-07 23:33:27 +02:00
# Path to pgp binary; for PGP 5.0, set the path to the pgpv binary.
1996-05-07 23:33:27 +02:00
$pgp = '/usr/local/bin/pgp';
# if you keep your keyring somewhere that is not the default used by pgp,
# uncomment the next line and set appropriately.
# $ENV{'PGPPATH'} = '/path/to/your/pgp/config';
$tmpdir = "/tmp";
1996-05-07 23:33:27 +02:00
### Exit value:
### 0 good signature
### 1 no signature
### 2 unknown signature
### 3 bad signature
### 255 problem not directly related to pgp analysis of signature
die "Usage: $0 < message\n" if @ARGV != 0;
$0 =~ s%^.*/%%; # trim /path/to/prog to prog
do '@ETCDIR@/innshellvars.pl';
$pgp = $inn'pgp if $inn'pgp && $inn'pgp ne "no-pgp-found-during-configure";
$tmp = ($inn'pathtmp ? $inn'pathtmp : $tmpdir) . "/pgp$$";
if (! -x $pgp) {
die "$0: $pgp: ", (-e _ ? "cannot execute" : "no such file"), "\n";
}
1996-05-07 23:33:27 +02:00
# this is, by design, case-sensitive with regards to the headers it checks.
# it's also insistent about the colon-space rule.
while (<>) {
# if a header line ends with \r\n, this article is in the encoding
# it would be in during an NNTP session. some article storage
# managers keep them this way for efficiency.
$nntp_format = /\r\n$/ if $. == 1;
s/\r?\n$//;
1996-05-07 23:33:27 +02:00
last if /^$/;
if (/^(\S+):[ \t](.+)/) {
($label, $value) = ($1, $2);
$dup{$label} = 1 if $header{$label};
$header{$label} = $value;
} elsif (/^\s/) {
&fail("$0: non-header line at $_\n") unless $label;
$header{$label} .= "\n$_";
} else {
&fail("$0: non-header line at $_\n");
}
}
$pgpheader = "X-PGP-Sig";
exit 1 unless $_ = $header{$pgpheader}; # no signature
# the regexp below might be too strict about the structure of pgp sig lines
# the $sep value means the separator between the radix64 signature lines
# can have any amount of spaces or tabs, but must have at least one space
# or tab, if there is a newline then the space or tab has to follow the
# newline. any number of newlines can appear as long as each is followed
# by at least one space or tab. *phew*
$sep = "[ \t]*(\n?[ \t]+)+";
# match all of the characters in a radix64 string
1996-05-07 23:33:27 +02:00
$r64 = '[a-zA-Z0-9+/]';
1996-05-07 23:33:27 +02:00
&fail("$0: $pgpheader not in expected format\n")
unless /^(\S+)$sep(\S+)(($sep$r64{64})+$sep$r64+=?=?$sep=$r64{4})$/;
1996-05-07 23:33:27 +02:00
($version, $signed_headers, $signature) = ($1, $3, $4);
$signature =~ s/$sep/\n/g;
1996-05-07 23:33:27 +02:00
$message = "-----BEGIN PGP SIGNED MESSAGE-----\n\n";
$message .= "X-Signed-Headers: $signed_headers\n";
foreach $label (split(",", $signed_headers)) {
&fail("$0: duplicate signed $label header, can't verify\n")
if $dup{$label};
$message .= "$label: ";
$message .= "$header{$label}" if $header{$label};
$message .= "\n";
}
$message .= "\n"; # end of headers
while (<>) { # read body lines
if ($nntp_format) {
# check for end of article; some news servers (eg, Highwind's "Breeze")
# include the dot-CRLF of the NNTP protocol in the article data passed
# to this script
last if $_ eq ".\r\n";
# remove NNTP encoding
s/^\.\./\./;
s/\r\n$/\n/;
}
1996-05-07 23:33:27 +02:00
s/^-/- -/; # pgp quote ("ASCII armor") dashes
$message .= $_; # append to output string
}
$message .= "\n-----BEGIN PGP SIGNATURE-----\n";
$message .= "Version: $version\n";
$message .= $signature;
$message .= "\n-----END PGP SIGNATURE-----\n";
open(TMP,">> $tmp") || &fail("$0: open > $tmp: $!\n");
-f TMP ||
&fail("$0: $tmp not a plain file, possible security violation attempt\n");
(stat(_))[3] == 1 ||
&fail("$0: $tmp has hard links, possible security violation attempt\n");
seek TMP, 0, 0; # make sure pointer is at beginning of file
truncate TMP, 0; # make sure file is zero length
1996-05-07 23:33:27 +02:00
print TMP $message;
close(TMP) || warn "$0: close > $tmp: $!\n";
&fail("$0: write error for message to check\n")
if -s $tmp != length($message);
print $message if $test;
1996-05-07 23:33:27 +02:00
$ok = 2; # unknown signature result is default
open(PGP,"$pgp -f +language=en < $tmp 2>&1 >/dev/null |") ||
1996-05-07 23:33:27 +02:00
&fail("$0: failed to execute pgp: $!\n");
undef $/;
$_ = <PGP>;
print if $test;
# MIT PGP 2.6.2:
# Good signature from user "Robert Braver <rbraver@ohww.norman.ok.us>".
# ViaCrypt PGP 4.0:
# Good signature from user: Robert Braver <rbraver@ohww.norman.ok.us>
# PGP 5.0i:
# Good signature made 1997-07-09 21:57 GMT by key:
# 1024 bits, Key ID B88DA9C1, Created 1996-04-10
# "news.announce.newgroups"
if (/Bad signature /) {
$ok = 3;
} elsif (/Good signature from user(: (.*)| "(.*)"\.)/ ||
/Good signature made .* by key:\n.+\n +"(.*)"/) {
$ok = 0;
$signer = $+;
} elsif (/Keyring file '(.*)' does not exist/) {
&fail("$0: couldn't access $1. Bad \$HOME or \$PGPPATH?\n");
1996-05-07 23:33:27 +02:00
}
1996-05-07 23:33:27 +02:00
close(PGP) || warn "$0: closing pgp pipe returned status $?\n";
unlink("$tmp") || warn "$0: unlink $tmp: $!\n";
print "$signer\n" if $signer;
exit $ok;
sub
fail
{
unlink($tmp);
print STDERR $_[0];
print STDERR " ... while processing $header{'Message-ID'}\n"
if $header{'Message-ID'};
1996-05-07 23:33:27 +02:00
exit 255;
}
# Our lawyer told me to include the following. The upshot of it is
# that you can use the software for free as much as you like.
# Copyright (c) 1996 UUNET Technologies, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
# must display the following acknowledgement:
# This product includes software developed by UUNET Technologies, Inc.
# 4. The name of UUNET Technologies ("UUNET") may not be used to endorse or
# promote products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY UUNET ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL UUNET BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.