A hacked copy of pgpverify 1.15 for testing attached signature
verification.
This commit is contained in:
parent
6117024200
commit
5ead5bfc33
1 changed files with 790 additions and 0 deletions
790
tests/pgpverify-old
Executable file
790
tests/pgpverify-old
Executable file
|
@ -0,0 +1,790 @@
|
|||
#! /usr/bin/perl -ws
|
||||
#
|
||||
##############################################################################
|
||||
# NOTE: This is an obsolete version of pgpverify included here for the
|
||||
# test suite because it was the last version of pgpverify that used
|
||||
# attached signatures. It remains part of the test suite to be sure that
|
||||
# using the attached method, PGP 2.6.x signatures generated by a modern
|
||||
# signcontrol still verify.
|
||||
#
|
||||
# It's not recommended for use for any other purpose, and has been hacked
|
||||
# to run within the test suite.
|
||||
##############################################################################
|
||||
#
|
||||
# written April 1996, tale@isc.org (David C Lawrence)
|
||||
# Currently maintained by Russ Allbery <rra@stanford.edu>
|
||||
# Version 1.15, 25 Nov 2002
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# This program is intended to be compatible with Perl 4 and Perl 5.
|
||||
#
|
||||
# Changes from 1.14 -> 1.15
|
||||
# -- Added POD documentation.
|
||||
# -- Fixed the -test switch so that it works again.
|
||||
# -- Dropped Perl 4 compatibility and reformatted. Now passes use strict.
|
||||
#
|
||||
# Changes from 1.13.1 -> 1.14
|
||||
# -- Native support for GnuPG without the pgpgpg wrapper, using GnuPG's
|
||||
# program interface by Marco d'Itri.
|
||||
# -- Always use Sys::Syslog without any setlogsock call for Perl 5.6.0 or
|
||||
# later, since Sys::Syslog in those versions of Perl uses the C library
|
||||
# interface and is now portable.
|
||||
# -- Default to expecting the key ring in $inn'newsetc/pgp if it exists.
|
||||
# -- Fix a portability problem for Perl 4 introduced in 1.12.
|
||||
#
|
||||
# Changes from 1.13 -> 1.13.1
|
||||
# -- Nothing functional, just moved the innshellvars.pl line to the head of
|
||||
# the script, to accomodate the build process of INN.
|
||||
#
|
||||
# Changes from 1.12 -> 1.13
|
||||
# -- Use INN's syslog_facility if available.
|
||||
#
|
||||
# Changes from 1.11 -> 1.12
|
||||
# -- support for GnuPG
|
||||
# -- Use /usr/ucb/logger, if present, instead of /usr/bin/logger (the latter
|
||||
# of which, on Solaris at least, is some sort of brain damaged POSIX.2
|
||||
# command which doesn't use syslog).
|
||||
# -- made syslog work for dec_osf (version 4, at least)
|
||||
# -- fixed up priority of '.' operator vs bitwise operators
|
||||
#
|
||||
# Changes from 1.10 -> 1.11
|
||||
# -- code to log error messages to syslog.
|
||||
# See $syslog and $syslog_method configurable variables.
|
||||
# -- configurably allow date stamp on stderr error messages.
|
||||
# -- added locking for multiple concurrent pgp instances.
|
||||
# -- more clear error message if pgp exits abnormally.
|
||||
# -- identify PGP 5 "BAD signature" string.
|
||||
# -- minor diddling for INN (path to innshellvars.pl changed)
|
||||
#
|
||||
# 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
|
||||
|
||||
# Path to the GnuPG gpgv binary, if you have GnuPG. If you do, this will
|
||||
# be used in preference to PGP. For most current control messages, you
|
||||
# need a version of GnuPG that can handle RSA signatures. If you have INN
|
||||
# and the script is able to successfully include your innshellvars.pl
|
||||
# file, the value of $inn::gpgv will override this.
|
||||
# $gpgv = '/usr/local/bin/gpgv';
|
||||
|
||||
# Path to pgp binary; for PGP 5.0, set the path to the pgpv binary.
|
||||
# If you have INN and the script is able to successfully include your
|
||||
# innshellvars.pl file, the value of $inn::pgp will override this.
|
||||
# $pgp = 'pgp';
|
||||
|
||||
# If you keep your keyring somewhere that is not the default used by pgp,
|
||||
# uncomment the next line and set appropriately. If you have INN and the
|
||||
# script is able to successfully include your innshellvars.pl file, this
|
||||
# will be set to $inn::newsetc/pgp if that directory exists unless you set
|
||||
# it explicitly. GnuPG will use a file named pubring.gpg in this
|
||||
# directory.
|
||||
$keyring = './keyring';
|
||||
|
||||
# If you have INN and the script is able to successfully include your
|
||||
# innshellvars.pl file, the value of $inn::pathtmp and $inn::locks will
|
||||
# override these.
|
||||
$tmpdir = "./tmp";
|
||||
$lockdir = $tmpdir;
|
||||
|
||||
# How should syslog be accessed?
|
||||
#
|
||||
# As it turns out, syslogging is very hard to do portably in versions of
|
||||
# Perl prior to 5.6.0. Sys::Syslog should work without difficulty in
|
||||
# 5.6.0 or later and will be used automatically for those versions of Perl
|
||||
# (unless $syslog_method is ''). For earlier versions of Perl, 'inet' is
|
||||
# all that's available up to version 5.004_03. If your syslog does not
|
||||
# accept UDP log packets, such as when syslogd runs with the -l flag,
|
||||
# 'inet' will not work. A value of 'unix' will try to contact syslogd
|
||||
# directly over a Unix domain socket built entirely in perl code (no
|
||||
# subprocesses). If that is not working for you, and you have the
|
||||
# 'logger' program on your system, set this variable to its full path name
|
||||
# to have a subprocess contact syslogd. If the method is just "logger",
|
||||
# the script will search some known directories for that program. If it
|
||||
# can't be found & used, everything falls back on stderr logging.
|
||||
#
|
||||
# You can test the script's syslogging by running "pgpverify < /some/text/file"
|
||||
# on a file that is not a valid news article. The "non-header at line #"
|
||||
# error should be syslogged.
|
||||
#
|
||||
# $syslog_method = 'unix'; # Unix doman socket, perl5.004_03 or higher
|
||||
# $syslog_method = 'inet'; # UDP to port 514 of localhost
|
||||
# $syslog_method = ''; # Don't ever try to do syslogging.
|
||||
$syslog_method = ''; # search for the logger program
|
||||
|
||||
# The next two variables are the values to be used for syslog's facility
|
||||
# and level to use, as would be found in syslog.conf. For various
|
||||
# reasons, it is impossible to economically have the script figure out how
|
||||
# to do syslogging correctly on the machine. If you have INN and the
|
||||
# script is able to successfully include you innshellvars.pl file, then
|
||||
# the value of $inn::syslog_facility will override this value of
|
||||
# $syslog_facility; $syslog_level is unaffected.
|
||||
$syslog_facility = 'news';
|
||||
$syslog_level = 'err';
|
||||
|
||||
# Prepend the error message with a timestamp?
|
||||
# This is only relevant if not syslogging, when errors go to stderr.
|
||||
#
|
||||
# $log_date = 0; # zero means don't do it.
|
||||
# $log_date = 1; # non-zero means do it.
|
||||
$log_date = -t STDOUT; # do it if STDOUT is to a terminal
|
||||
|
||||
# End of configuration section.
|
||||
|
||||
|
||||
require 5;
|
||||
|
||||
use strict;
|
||||
use vars qw($gpgv $pgp $keyring $tmp $tmpdir $lockdir $syslog_method
|
||||
$syslog_facility $syslog_level $log_date $test $messageid);
|
||||
|
||||
# Hack added for test suite.
|
||||
$pgp = shift;
|
||||
|
||||
# Turn on test mode if the first argument is '-test'.
|
||||
if ($1 && $1 eq '-test') {
|
||||
shift @ARGV;
|
||||
$test = 1;
|
||||
}
|
||||
|
||||
# Not syslogged, such an error is almost certainly from someone running
|
||||
# the script manually.
|
||||
die "Usage: $0 < message\n" if @ARGV != 0;
|
||||
|
||||
# Grab various defaults from innshellvars.pl if running inside INN.
|
||||
$pgp = $inn::pgp
|
||||
if $inn::pgp && $inn::pgp ne "no-pgp-found-during-configure";
|
||||
$gpgv = $inn::gpgv if $inn::gpgv;
|
||||
$tmp = ($inn::pathtmp ? $inn::pathtmp : $tmpdir) . "/pgp$$";
|
||||
$lockdir = $inn::locks if $inn::locks;
|
||||
$syslog_facility = $inn::syslog_facility if $inn::syslog_facility;
|
||||
if (! $keyring && $inn::newsetc) {
|
||||
$keyring = $inn::newsetc . '/pgp' if -d $inn::newsetc . '/pgp';
|
||||
}
|
||||
|
||||
# Trim /path/to/prog to prog for error messages.
|
||||
$0 =~ s%^.*/%%;
|
||||
|
||||
# Make sure that the signature verification program can be executed.
|
||||
if ($gpgv) {
|
||||
if (! -x $gpgv) {
|
||||
&fail("$0: $gpgv: " . (-e _ ? "cannot execute" : "no such file") . "\n");
|
||||
}
|
||||
} elsif (! -x $pgp) {
|
||||
&fail("$0: $pgp: " . (-e _ ? "cannot execute" : "no such file") . "\n");
|
||||
}
|
||||
|
||||
# Parse the article headers and generate the PGP message.
|
||||
my ($nntp_format, $header, $dup) = &parse_header();
|
||||
exit 1 unless $$header{'X-PGP-Sig'};
|
||||
my $message = &generate_message($nntp_format, $header, $dup);
|
||||
&write_message($message);
|
||||
|
||||
# Verify the message.
|
||||
my ($ok, $signer);
|
||||
if ($gpgv) {
|
||||
($ok, $signer) = &gpg_check($tmp, $keyring);
|
||||
} else {
|
||||
($ok, $signer) = &pgp_check($tmp, $keyring);
|
||||
}
|
||||
print "$signer\n" if $signer;
|
||||
exit $ok;
|
||||
|
||||
|
||||
# Parse the article headers and return a flag saying whether the message
|
||||
# is in NNTP format and then two references to hashes. The first hash
|
||||
# contains all the header/value pairs, and the second contains entries for
|
||||
# every header that's duplicated. This is, by design, case-sensitive with
|
||||
# regards to the headers it checks. It's also insistent about the
|
||||
# colon-space rule.
|
||||
sub parse_header {
|
||||
my (%header, %dup, $label, $value);
|
||||
while (<>) {
|
||||
# If the first 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.
|
||||
my $nntp_format = /\r\n$/ if $. == 1;
|
||||
s/\r?\n$//;
|
||||
|
||||
last if /^$/;
|
||||
if (/^(\S+):[ \t](.+)/) {
|
||||
($label, $value) = ($1, $2);
|
||||
$dup{$label} = 1 if $header{$label};
|
||||
$header{$label} = $value;
|
||||
} elsif (/^\s/) {
|
||||
&fail("$0: non-header at line $.: $_\n") unless $label;
|
||||
$header{$label} .= "\n$_";
|
||||
} else {
|
||||
&fail("$0: non-header at line $.: $_\n");
|
||||
}
|
||||
}
|
||||
$messageid = $header{'Message-ID'};
|
||||
return ($nntp_format, \%header, \%dup);
|
||||
}
|
||||
|
||||
# Generate the PGP message to verify, undoing the same transformation as
|
||||
# is applied by signcontrol (along with other changes required to deal
|
||||
# with NNTP wire format and to quote the message properly for PGP). Takes
|
||||
# the hash of headers and header duplicates returned by parse_header.
|
||||
sub generate_message {
|
||||
my ($nntp_format, $header, $dup) = @_;
|
||||
|
||||
# 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*
|
||||
my $sep = "[ \t]*(\n?[ \t]+)+";
|
||||
|
||||
# Match all of the characters in a radix64 string
|
||||
my $r64 = '[a-zA-Z0-9+/]';
|
||||
|
||||
local $_ = $$header{'X-PGP-Sig'};
|
||||
&fail("$0: X-PGP-Sig not in expected format\n")
|
||||
unless /^(\S+)$sep(\S+)(($sep$r64{64})+$sep$r64+=?=?$sep=$r64{4})$/;
|
||||
|
||||
my ($version, $signed_headers, $signature) = ($1, $3, $4);
|
||||
$signature =~ s/$sep/\n/g;
|
||||
|
||||
my $message = "-----BEGIN PGP SIGNED MESSAGE-----\n\n";
|
||||
$message .= "X-Signed-Headers: $signed_headers\n";
|
||||
my $label;
|
||||
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/;
|
||||
}
|
||||
|
||||
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";
|
||||
return $message;
|
||||
}
|
||||
|
||||
# Write a PGP message to a file. Attempt to do so safely.
|
||||
sub write_message {
|
||||
my ($message) = @_;
|
||||
|
||||
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
|
||||
|
||||
print TMP $message;
|
||||
close(TMP) || &errmsg("$0: close > $tmp: $!\n");
|
||||
&fail("$0: write error for message to check\n")
|
||||
if -s $tmp != length($message);
|
||||
|
||||
print $message if $test;
|
||||
}
|
||||
|
||||
# Check the signature using PGP (including 2.6.2, 5.0, and the pgpgpg
|
||||
# wrapper for GnuPG).
|
||||
sub pgp_check {
|
||||
my ($file, $ring) = @_;
|
||||
|
||||
$ENV{'PGPPATH'} = $ring if $ring;
|
||||
|
||||
# The call to pgp needs to be locked because it tries to both read and
|
||||
# write a file named randseed.bin but doesn't do its own locking as it
|
||||
# should, and the consequences of a multiprocess conflict is failure to
|
||||
# verify.
|
||||
my $lock = "$lockdir/LOCK.$0";
|
||||
|
||||
until (&shlock($lock) > 0) {
|
||||
sleep(2);
|
||||
}
|
||||
|
||||
open(PGP,"$pgp -f +language=en < $file 2>&1 >/dev/null |") ||
|
||||
&fail("$0: failed to execute pgp: $!\n");
|
||||
|
||||
undef $/;
|
||||
$_ = <PGP>;
|
||||
|
||||
unlink($lock) || &errmsg("$0: unlink $lock: $!\n");
|
||||
unlink($file) || &errmsg("$0: unlink $file: $!\n");
|
||||
|
||||
unless (close(PGP)) {
|
||||
if ($? >> 8) {
|
||||
&errmsg("$0: pgp exited status " . ($? >> 8) . "\n");
|
||||
} else {
|
||||
&errmsg("$0: pgp died on signal " . ($? & 255) . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
# GnuPG (via pgpgpg)
|
||||
# Good signature from "news.announce.newgroups"
|
||||
# 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"
|
||||
|
||||
my $ok = 2; # unknown signature result is default
|
||||
my $signer;
|
||||
if (/B[Aa][Dd] signature /) {
|
||||
$ok = 3;
|
||||
} elsif (/Good signature from user(: (.*)| "(.*)"\.)/ ||
|
||||
/Good signature from "(.*)"/ ||
|
||||
/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");
|
||||
}
|
||||
|
||||
return ($ok, $signer);
|
||||
}
|
||||
|
||||
# Check the signature using GnuPG.
|
||||
sub gpg_check {
|
||||
my ($file, $ring) = @_;
|
||||
|
||||
my $opts = '--quiet --status-fd=1 --logger-fd=1';
|
||||
if ($ring) {
|
||||
$opts .= " --keyring=$ring/pubring.gpg";
|
||||
} else {
|
||||
$opts .= ' --keyring=pubring.gpg';
|
||||
}
|
||||
|
||||
open(PGP, "$gpgv $opts $file 2> /dev/null |") ||
|
||||
&fail("$0: failed to execute $gpgv: $!\n");
|
||||
|
||||
undef $/;
|
||||
$_ = <PGP>;
|
||||
|
||||
unlink($file) || &errmsg("$0: unlink $file: $!\n");
|
||||
|
||||
unless (close(PGP)) {
|
||||
if ($? >> 8) {
|
||||
&errmsg("$0: gpgv exited status " . ($? >> 8) . "\n");
|
||||
} else {
|
||||
&errmsg("$0: gpgv died on signal " . ($? & 255) . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
print if $test;
|
||||
|
||||
my $ok = 255; # default exit status
|
||||
my $signer;
|
||||
if (/\[GNUPG:\]\s+GOODSIG\s+\S+\s+(\S+)/) {
|
||||
$ok = 0;
|
||||
$signer = $1;
|
||||
} elsif (/\[GNUPG:\]\s+NODATA/ || /\[GNUPG:\]\s+UNEXPECTED/) {
|
||||
$ok = 1;
|
||||
} elsif (/\[GNUPG:\]\s+NO_PUBKEY/) {
|
||||
$ok = 2;
|
||||
} elsif (/\[GNUPG:\]\s+BADSIG\s+/) {
|
||||
$ok = 3;
|
||||
}
|
||||
|
||||
return ($ok, $signer);
|
||||
}
|
||||
|
||||
# Log an error message, attempting syslog first based on $syslog_method
|
||||
# and falling back on stderr.
|
||||
sub errmsg {
|
||||
my ($message) = @_;
|
||||
$message =~ s/\n$//;
|
||||
|
||||
my $date = '';
|
||||
if ($log_date) {
|
||||
($date = localtime) =~ s/\d{4}\n//;
|
||||
}
|
||||
|
||||
if ($syslog_method && $] >= 5.006) {
|
||||
eval "use Sys::Syslog";
|
||||
$syslog_method = 'internal';
|
||||
}
|
||||
|
||||
if ($syslog_method eq "logger") {
|
||||
my @loggers = ('/usr/ucb/logger', '/usr/bin/logger',
|
||||
'/usr/local/bin/logger');
|
||||
my $try;
|
||||
foreach $try (@loggers) {
|
||||
if (-x $try) {
|
||||
$syslog_method = $try;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$syslog_method = '' if $syslog_method eq 'logger';
|
||||
}
|
||||
|
||||
if ($syslog_method ne '' && $syslog_method !~ m%/logger$%) {
|
||||
eval "use Sys::Syslog";
|
||||
}
|
||||
|
||||
if ($@ || $syslog_method eq '') {
|
||||
warn $date, "$0: trying to use perl's syslog: $@\n" if $@;
|
||||
warn $date, $message, "\n";
|
||||
warn $date, "... while processing $messageid\n"
|
||||
if $messageid;
|
||||
|
||||
} else {
|
||||
$message .= " processing $messageid"
|
||||
if $messageid;
|
||||
|
||||
if ($syslog_method =~ m%/logger$%) {
|
||||
unless (system($syslog_method, "-i", "-p",
|
||||
"$syslog_facility.$syslog_level", $message) == 0) {
|
||||
if ($? >> 8) {
|
||||
warn $date, "$0: $syslog_method exited status ", $? >> 8, "\n";
|
||||
} else {
|
||||
warn $date, "$0: $syslog_method died on signal ", $? & 255, "\n";
|
||||
}
|
||||
$syslog_method = '';
|
||||
&errmsg($message);
|
||||
}
|
||||
|
||||
} else {
|
||||
# setlogsock arrived in perl 5.004_03 to enable Sys::Syslog to use a
|
||||
# Unix domain socket to talk to syslogd, which is the only way to do
|
||||
# it when syslog runs with the -l switch.
|
||||
if ($syslog_method eq "unix") {
|
||||
if ($^O eq "dec_osf" && $] >= 5) {
|
||||
eval 'sub Sys::Syslog::_PATH_LOG { "/dev/log" }';
|
||||
}
|
||||
if ($] <= 5.00403 || ! eval "setlogsock('unix')") {
|
||||
warn $date, "$0: cannot use syslog_method 'unix' on this system\n";
|
||||
$syslog_method = '';
|
||||
&errmsg($message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Unfortunately, there is no way to definitively know in this
|
||||
# program if the message was logged. I wish there were a way to
|
||||
# send a message to stderr if and only if the syslog attempt failed.
|
||||
&openlog($0, 'pid', $syslog_facility);
|
||||
&syslog($syslog_level, $_[0]);
|
||||
&closelog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub fail {
|
||||
unlink($tmp);
|
||||
&errmsg($_[0]);
|
||||
exit 255;
|
||||
}
|
||||
|
||||
# Get a lock in essentially the same fashion as INN's shlock. return 1 on
|
||||
# success, 0 for normal failure, -1 for abnormal failure. "normal
|
||||
# failure" is that a lock is apparently in use by someone else.
|
||||
sub shlock {
|
||||
my ($file) = @_;
|
||||
my ($ltmp, $pid);
|
||||
|
||||
unless (defined(&ENOENT)) {
|
||||
eval "require POSIX qw(:errno_h)";
|
||||
if ($@) {
|
||||
# values taken from BSD/OS 3.1
|
||||
sub ENOENT { 2 }
|
||||
sub ESRCH { 3 }
|
||||
sub EEXIST { 17 }
|
||||
}
|
||||
}
|
||||
|
||||
$ltmp = ($file =~ m%(.*/)%)[0] . "shlock$$";
|
||||
|
||||
# this should really attempt to use another temp name
|
||||
-e $ltmp && (unlink($ltmp) || return -1);
|
||||
|
||||
open(LTMP, ">$ltmp") || return -1;
|
||||
print LTMP "$$\n" || (unlink($ltmp), return -1);
|
||||
close(LTMP) || (unlink($ltmp), return -1);
|
||||
|
||||
if (!link($ltmp, $file)) {
|
||||
if ($! == &EEXIST) {
|
||||
if (open(LOCK, "<$file")) {
|
||||
$pid = <LOCK>;
|
||||
if ($pid =~ /^\d+$/ && (kill(0, $pid) == 1 || $! != &ESRCH)) {
|
||||
unlink($ltmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
# ok, the pid in the lockfile is not a number or no longer exists.
|
||||
close(LOCK); # silent failure is ok here
|
||||
|
||||
# unlink failed
|
||||
if (unlink($file) != 1 && $! != &ENOENT) {
|
||||
unlink($ltmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
# check if open failed for reason other than file no longer present
|
||||
} elsif ($! != &ENOENT) {
|
||||
unlink($ltmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
# either this process unlinked the lockfile because it was bogus,
|
||||
# or between this process's link() and open() the other process
|
||||
# holding the lock unlinked it. This process can now try to aquire.
|
||||
if (! link($ltmp, $file)) {
|
||||
unlink($ltmp);
|
||||
return $! == &EEXIST ? 0 : -1; # maybe another proc grabbed the lock
|
||||
}
|
||||
|
||||
} else { # first attempt to link failed
|
||||
unlink($ltmp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
unlink($ltmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head1 NAME
|
||||
|
||||
pgpverify - Cryptographically verify Usenet control messages
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<pgpverify> [B<-test>] < I<message>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The B<pgpverify> program reads (on standard input) a Usenet control
|
||||
message that has been cryptographically signed using the B<signcontrol>
|
||||
program (or some other program that produces a compatible format).
|
||||
B<pgpverify> then uses a PGP implementation to determine who signed the
|
||||
control message. If the control message has a valid signature,
|
||||
B<pgpverify> prints (to stdout) the user ID of the key that signed the
|
||||
message. Otherwise, it exits with a non-zero exit status.
|
||||
|
||||
If B<pgpverify> is installed as part of INN, it uses INN's configuration
|
||||
to determine what signature verification program to use, how to log
|
||||
errors, what temporary directory to use, and what keyring to use.
|
||||
Otherwise, all of those parameters can be set by editing the beginning of
|
||||
this script.
|
||||
|
||||
By default, when running as part of INN, B<pgpverify> expects the PGP key
|
||||
ring to be found in I<pathetc>/pgp (as either F<pubring.pgp> or
|
||||
F<pubring.gpg> depending on whether PGP or GnuPG is used to verify
|
||||
signatures). If that directory doesn't exist, it will fall back on using
|
||||
the default key ring, which is in a F<.pgp> or F<.gnupg> subdirectory of
|
||||
the running user's home directory.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
The B<-test> flag causes B<pgpverify> to print out the input that it is
|
||||
passing to PGP (which is a reconstructed version of the input that
|
||||
supposedly created the control message) as well as the output from PGP's
|
||||
analysis of the message.
|
||||
|
||||
=head1 EXIT STATUS
|
||||
|
||||
B<pgpverify> may exit with the following statuses:
|
||||
|
||||
=over 5
|
||||
|
||||
=item 0Z<>
|
||||
|
||||
The control message had a good PGP signature.
|
||||
|
||||
=item 1
|
||||
|
||||
The control message had no PGP signature.
|
||||
|
||||
=item 2
|
||||
|
||||
The control message had an unknown PGP signature.
|
||||
|
||||
=item 3
|
||||
|
||||
The control message had a bad PGP signature.
|
||||
|
||||
=item 255
|
||||
|
||||
A problem occurred not directly related to PGP analysis of signature.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ENVIRONMENT
|
||||
|
||||
B<pgpverify> does not modify or otherwise alter the environment before
|
||||
invoking the B<pgp> or B<gpgv> program. It is the responsibility of the
|
||||
person who installs B<pgpverify> to ensure that when B<pgp> or B<gpgv>
|
||||
runs, it has the ability to locate and read a PGP key file that contains
|
||||
the PGP public keys for the appropriate Usenet hierarchy administrators.
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
Historically, Usenet news server administrators have configured their news
|
||||
servers to automatically honor Usenet control messages based on the
|
||||
originator of the control messages and the hierarchies for which the
|
||||
control messages applied. For example, in the past, David Lawrence always
|
||||
issued control messages for the "Big 8" hierarchies (comp, humanities,
|
||||
misc, news, rec, sci, soc, talk). Usenet news administrators would
|
||||
configure their news server software to automatically honor newgroup and
|
||||
rmgroup control messages that originated from David Lawrence and applied
|
||||
to any of the Big 8 hierarchies.
|
||||
|
||||
Unfortunately, Usenet news articles (including control messages) are
|
||||
notoriously easy to forge. Soon, malicious users realized they could
|
||||
create or remove (at least temporarily) any Big 8 newsgroup they wanted by
|
||||
simply forging an appropriate control message in David Lawrence's name.
|
||||
As Usenet became more widely used, forgeries became more common.
|
||||
|
||||
The B<pgpverify> program was designed to allow Usenet news administrators
|
||||
to configure their servers to cryptographically verify control messages
|
||||
before automatically acting on them. Under the pgpverify system, a Usenet
|
||||
hierarchy maintainer creates a PGP public/private key pair and
|
||||
disseminates the public key. Whenever the hierarchy maintainer issues a
|
||||
control message, he uses the B<signcontrol> program to sign the control
|
||||
message with the PGP private key. Usenet news administrators configure
|
||||
their news servers to run the B<pgpverify> program on the appropriate
|
||||
control messages, and take action based on the PGP key User ID that signed
|
||||
the control message, not the name and address that appear in the control
|
||||
message's From or Sender headers.
|
||||
|
||||
Thus, using the B<signcontrol> and B<pgpverify> programs appropriately
|
||||
essentially eliminates the possibility of malicious users forging Usenet
|
||||
control messages that sites will act upon, as such users would have to
|
||||
obtain the PGP private key in order to forge a control message that would
|
||||
pass the cryptographic verification step. If the hierarchy administrators
|
||||
properly protect their PGP private keys, the only way a malicious user
|
||||
could forge a validly-signed control message would be by breaking the
|
||||
public key encryption algorithm, which (at least at this time) is believed
|
||||
to be prohibitively difficult for PGP keys of a sufficient bit length.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
gpgv(1), pgp(1)
|
||||
|
||||
L<ftp://ftp.isc.org/pub/pgpcontrol/> is where the most recent versions of
|
||||
B<signcontrol> and B<pgpverify> live, along with PGP public keys used for
|
||||
hierarchy administration.
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
B<pgpverify> was written by David C Lawrence <tale@isc.org>. Manual page
|
||||
provided by James Ralston.
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
David Lawrence wrote: "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:
|
||||
|
||||
=over 3
|
||||
|
||||
=item 1.
|
||||
|
||||
Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
=item 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.
|
||||
|
||||
=item 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.
|
||||
|
||||
=item 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.
|
||||
|
||||
=back
|
||||
|
||||
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.
|
||||
|
||||
=cut
|
||||
|
||||
# Local variables:
|
||||
# cperl-indent-level: 2
|
||||
# fill-column: 74
|
||||
# End:
|
Loading…
Reference in a new issue