--- /dev/null
+++ b/extra/gpgverify
@@ -0,0 +1,230 @@
+#!/usr/bin/perl -w
+require '/usr/lib/news/innshellvars.pl';
+
+# written April 1996, tale@isc.org (David C Lawrence)
+# mostly rewritten 2001-03-21 by Marco d'Itri <md@linux.it>
+#
+# requirements:
+# - GnuPG
+# - perl 5.004_03 and working Sys::Syslog
+# - syslog daemon
+#
+# There is no locking because gpg is supposed to not need it and controlchan
+# will serialize control messages processing anyway.
+
+require 5.004_03;
+use strict;
+
+# if you keep your keyring somewhere that is not the default used by gpg,
+# set appropriately the next line.
+my $keyring = '/etc/news/pgp/pubring.pgp';
+
+# If you have INN and the script is able to successfully include your
+# innshellvars.pl file, the value of the next two variables will be
+# overridden.
+my $tmpdir = '/var/log/news/';
+my $syslog_facility = 'news';
+
+# 1: print PGP output
+my $debug = 0;
+$debug = 1 if -t 1;
+
+### Exit value:
+### 0  good signature
+### 1  no signature
+### 2  unknown signature
+### 3  bad signature
+### 255 problem not directly related to gpg analysis of signature
+
+##############################################################################
+################ NO USER SERVICEABLE PARTS BELOW THIS COMMENT ################
+##############################################################################
+my $tmp = ($inn::pathtmp ? $inn::pathtmp : $tmpdir) . "/pgp$$";
+$syslog_facility = $inn::syslog_facility if $inn::syslog_facility;
+
+my $nntp_format = 0;
+$0 =~ s#^.*/##;				# trim /path/to/prog to prog
+
+die "Usage: $0 < message\n" if $#ARGV != -1;
+
+# Path to gpg binary
+my $gpg;
+foreach (split(/:/, $ENV{PATH}), qw(/usr/local/bin /opt/gnu/bin)) {
+	if (-x "$_/gpgv") {
+		$gpg = "$_/gpgv"; last;
+	}
+}
+fail('cannot find the gpgv binary') if not $gpg;
+
+# this is, by design, case-sensitive with regards to the headers it checks.
+# it's also insistent about the colon-space rule.
+my ($label, $value, %dup, %header);
+while (<STDIN>) {
+	# 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$//;
+
+	last if /^$/;
+	if (/^(\S+):[ \t](.+)/) {
+		($label, $value) = ($1, $2);
+		$dup{$label} = 1 if $header{$label};
+		$header{$label} = $value;
+	} elsif (/^\s/) {
+		fail("non-header at line $.: $_") unless $label;
+		$header{$label} .= "\n$_";
+	} else {
+		fail("non-header at line $.: $_");
+	}
+}
+
+my $pgpheader = 'X-PGP-Sig';
+$_ = $header{$pgpheader};
+exit 1 if not $_; # no signature
+
+# 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+/]';
+fail("$pgpheader not in expected format")
+	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"
+			. "X-Signed-Headers: $signed_headers\n";
+
+foreach $label (split(',', $signed_headers)) {
+	fail("duplicate signed $label header, can't verify") if $dup{$label};
+	$message .= "$label: ";
+	$message .= $header{$label} if $header{$label};
+	$message .= "\n";
+}
+$message .= "\n";				# end of headers
+
+while (<STDIN>) {				# 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 .= $_;	
+}
+
+$message .=
+	"\n-----BEGIN PGP SIGNATURE-----\n" .
+	"Version: $version\n" .
+	$signature .
+	"\n-----END PGP SIGNATURE-----\n";
+
+open(TMP, ">$tmp") or fail("open $tmp: $!");
+print TMP $message;
+close TMP or errmsg("close $tmp: $!");
+
+my $opts = '--quiet --status-fd=1 --logger-fd=1';
+$opts .= " --keyring=$keyring" if $keyring;
+
+open(PGP, "$gpg $opts $tmp |") or fail("failed to execute $gpg: $!");
+
+undef $/;
+$_ = <PGP>;
+
+unlink $tmp or errmsg("unlink $tmp: $!");
+
+if (not close PGP) {
+	if ($? >> 8) {
+		my $status = $? >> 8;
+		errmsg("gpg exited status $status") if $status > 1;
+	} else {
+		errmsg('gpg died on signal ' . ($? & 255));
+	}
+}
+
+print STDERR $_ if $debug;
+
+my $ok = 255;		# default exit status
+my $signer;
+if (/^\[GNUPG:\]\s+GOODSIG\s+\S+\s+(\S+)/m) {
+	$ok = 0;
+	$signer = $1;
+} elsif (/^\[GNUPG:\]\s+NODATA/m or /^\[GNUPG:\]\s+UNEXPECTED/m) {
+	$ok = 1;
+} elsif (/^\[GNUPG:\]\s+NO_PUBKEY/m) {
+	$ok = 2;
+} elsif (/^\[GNUPG:\]\s+BADSIG\s+/m) {
+	$ok = 3;
+}
+
+print "$signer\n" if $signer;
+exit $ok;
+
+sub errmsg {
+	my $msg = $_[0];
+
+	eval 'use Sys::Syslog qw(:DEFAULT setlogsock)';
+	die "$0: cannot use Sys::Syslog: $@ [$msg]\n" if $@;
+	
+	die "$0: cannot set syslog method [$msg]\n"
+		if not (setlogsock('unix') or setlogsock('inet'));
+
+	$msg .= " processing $header{'Message-ID'}" if $header{'Message-ID'};
+
+	openlog($0, 'pid', $syslog_facility);
+	syslog('err', '%s', $msg);
+	closelog();
+}
+
+sub fail {
+	errmsg($_[0]);
+	unlink $tmp;
+	exit 255;
+}
+
+__END__
+
+# Copyright 2000 by Marco d'Itri
+
+# License of the original version distributed by David C. Lawrence:
+
+# 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.
--- /dev/null
+++ b/extra/pgpverify.8
@@ -0,0 +1,118 @@
+.\"
+.\" $Id: pgpverify.8,v 1.1 1999/06/21 16:44:22 bdale Exp $
+.\"
+.\" This manual page was provided by James Ralston <qralston+@pitt.edu>
+.TH pgpverify 8
+.SH NAME
+pgpverify - cryptographically verify Usenet control messages
+.SH SYNOPSIS
+.B pgpverify
+.SH DESCRIPTION
+The
+.I pgpverify
+program reads (on standard input) a Usenet control message that has
+been cryptographically signed using the
+.I signcontrol
+program.
+.I pgpverify
+then uses the
+.I pgp
+program to determine who signed the control message.  If the control
+message was validly signed,
+.I pgpverify
+outputs (to stdout) the User ID of the key ID that signed the message.
+.SH OPTIONS
+The
+.I pgpverify
+program takes no options.
+.SH EXIT STATUS
+.I pgpverify
+returns the follow exit statuses for the following cases:
+.P
+.TP
+.B 0
+The control message had a good PGP signature.
+.TP
+.B 1
+The control message had no PGP signature.
+.TP
+.B 2
+The control message had an unknown PGP signature.
+.TP
+.B 3
+The control message had a bad PGP signature.
+.TP
+.B 255
+A problem occurred not directly related to PGP analysis of signature.
+.SH AUTHOR
+David C Lawrence <tale@isc.org>
+.SH ENVIRONMENT
+.I pgpverify
+does not modify or otherwise alter the environment before invoking the
+.I pgp
+program.  It is the responsibility of the person who installs
+.I pgpverify
+to ensure that when
+.I pgp
+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.
+.SH SEE ALSO
+pgp(1)
+.SH 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 C
+Lawrence <tale@uunet.uu.net> 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.
+.P
+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.
+.P
+The
+.I 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
+.I signcontrol
+program to sign the control message with the PGP private key.  Usenet
+news administrators configure their news servers to run the
+.I 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.
+.P
+Thus, using the
+.I signcontrol
+and
+.I 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 RSA encryption algorithm,
+which (at least at this time) is believed to be an NP-complete
+problem.  If this is indeed the case, discovering the PGP private key
+based on the PGP public key is computationally impossible for PGP keys
+of a sufficient bit length.
+.P
+<URL:ftp://ftp.isc.org/pub/pgpcontrol/> is where the most recent
+versions of
+.I signcontrol
+and
+.I pgpverify
+live, along with PGP public keys used for hierarchy administration.
