#! /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 # Path to pgp binary; for PGP 5.0, set the path to the pgpv binary. $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"; ### 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"; } # 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$//; 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 $r64 = '[a-zA-Z0-9+/]'; &fail("$0: $pgpheader not in expected format\n") unless /^(\S+)$sep(\S+)(($sep$r64{64})+$sep$r64+=?=?$sep=$r64{4})$/; ($version, $signed_headers, $signature) = ($1, $3, $4); $signature =~ s/$sep/\n/g; $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/; } 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 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; $ok = 2; # unknown signature result is default open(PGP,"$pgp -f +language=en < $tmp 2>&1 >/dev/null |") || &fail("$0: failed to execute pgp: $!\n"); undef $/; $_ = ; print if $test; # MIT PGP 2.6.2: # Good signature from user "Robert Braver ". # ViaCrypt PGP 4.0: # Good signature from user: Robert Braver # 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"); } 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'}; 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.