Added support for GnuPG. Replace signing code with code from PGP::Sign
that generates detached signatures instead. Otherwise, GnuPG signatures with DSA keys could not be verified. Should still work the same as before with RSA keys. Thanks to new signing code, no longer uses a temporary file. Only lock when using PGP; GnuPG shouldn't need it.
This commit is contained in:
parent
1c4a1cb3d3
commit
ed17c7cb23
1 changed files with 235 additions and 120 deletions
337
signcontrol
337
signcontrol
|
@ -1,32 +1,49 @@
|
||||||
#!/usr/local/bin/perl5 -w
|
#! /usr/bin/perl -w
|
||||||
# written April 1996, tale@isc.org (David C Lawrence)
|
# written April 1996, tale@isc.org (David C Lawrence)
|
||||||
# Version 1.6, 14 October 1998
|
# Currently maintained by Russ Allbery <rra@stanford.edu>
|
||||||
|
# Version 1.8, 2003-07-06
|
||||||
#
|
#
|
||||||
# Changes from 1.5 to 1.6:
|
# Changes from 1.6 -> 1.8
|
||||||
# -- eliminated subprocess use (except pgp, of course)
|
# -- Added support for GnuPG.
|
||||||
# -- interlock against competing signing processes
|
# -- Replace signing code with code from PGP::Sign that generates detached
|
||||||
# -- allow optional headers; see $use_or_add
|
# signatures instead. Otherwise, GnuPG signatures with DSA keys could
|
||||||
# -- added simple comments about why particular headers are signed
|
# not be verified. Should still work the same as before with RSA keys.
|
||||||
|
# -- Thanks to new signing code, no longer uses a temporary file.
|
||||||
|
# -- Only lock when using PGP; GnuPG shouldn't need it.
|
||||||
|
#
|
||||||
|
# Changes from 1.5 -> 1.6
|
||||||
|
# -- eliminated subprocess use (except pgp, of course).
|
||||||
|
# -- interlock against competing signing processes.
|
||||||
|
# -- allow optional headers; see $use_or_add.
|
||||||
|
# -- added simple comments about why particular headers are signed.
|
||||||
# -- made error messages a tad more helpful for situations when it is hard
|
# -- made error messages a tad more helpful for situations when it is hard
|
||||||
# to know what message was trying to be signed (such as via an "at" job)
|
# to know what message was trying to be signed (such as via an "at"
|
||||||
|
# job).
|
||||||
# -- set $action, $group, $moderated to "" to prevent unusued variable
|
# -- set $action, $group, $moderated to "" to prevent unusued variable
|
||||||
# warnings in the event a Control header can't be parsed
|
# warnings in the event a Control header can't be parsed.
|
||||||
# -- moved assignment of $pgpend out of loop.
|
# -- moved assignment of $pgpend out of loop.
|
||||||
# Changes from 1.4 to 1.5
|
#
|
||||||
# -- need to require Text::Tabs to get 'expand' for tabs in checkgroups
|
# Changes from 1.4 -> 1.5
|
||||||
# Changes from 1.3 to 1.4:
|
# -- need to require Text::Tabs to get 'expand' for tabs in checkgroups.
|
||||||
# -- added checkgroups checking
|
#
|
||||||
# -- added group name in several error messages (for help w/batch processing)
|
# Changes from 1.3 -> 1.4
|
||||||
# -- disabled moderator address checking
|
# -- added checkgroups checking.
|
||||||
# -- adjusted newsgroups line (ie, tabbing fixed) now correctly substituted
|
# -- added group name in several error messages (for help w/batch
|
||||||
# into control message.
|
# processing).
|
||||||
# Changes from 1.2.3 to 1.3:
|
# -- disabled moderator address checking.
|
||||||
# -- skip minor pgp signature headers like "charset:" after "version:" header
|
# -- adjusted newsgroups line (ie, tabbing fixed) now correctly
|
||||||
# and until the empty line that starts the base64 signature block
|
# substituted into control message.
|
||||||
|
#
|
||||||
|
# Changes from 1.2.3 -> 1.3
|
||||||
|
# -- skip minor pgp signature headers like "charset:" after "version:"
|
||||||
|
# header and until the empty line that starts the base64 signature block.
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
# pgp variables.
|
# PGP variables.
|
||||||
|
#
|
||||||
|
# $pgp can be set to the path to GnuPG to use GnuPG instead. The program
|
||||||
|
# name needs to end in gpg so that signcontrol knows GnuPG is being used.
|
||||||
#
|
#
|
||||||
# STORING YOUR PASS PHRASE IN A FILE IS A POTENTIAL SECURITY HOLE.
|
# STORING YOUR PASS PHRASE IN A FILE IS A POTENTIAL SECURITY HOLE.
|
||||||
# make sure you know what you're doing if you do it.
|
# make sure you know what you're doing if you do it.
|
||||||
|
@ -37,23 +54,25 @@
|
||||||
#
|
#
|
||||||
# $pgplock is used because pgp does not guard itself against concurrent
|
# $pgplock is used because pgp does not guard itself against concurrent
|
||||||
# read/write access to its randseed.bin file. A writable file is needed;
|
# read/write access to its randseed.bin file. A writable file is needed;
|
||||||
# The default value is to use the .pgp/config.txt file in the home directory
|
# The default value is to use the .pgp/config.txt file in the home
|
||||||
# of the user running the program. Note that this will only work to lock
|
# directory of the user running the program. Note that this will only
|
||||||
# against other instances of signcontrol, not all pgp uses.
|
# work to lock against other instances of signcontrol, not all pgp uses.
|
||||||
|
# $pgplock is not used if $pgp ends in 'gpg' since GnuPG doesn't need
|
||||||
|
# this.
|
||||||
$pgpsigner = 'INSERT_YOUR_PGP_USERID';
|
$pgpsigner = 'INSERT_YOUR_PGP_USERID';
|
||||||
$pgppassfile = ''; # file with pass phrase for $pgpsigner
|
$pgppassfile = ''; # file with pass phrase for $pgpsigner
|
||||||
$pgp = "/usr/local/bin/pgp";
|
$pgp = "/usr/local/bin/pgp";
|
||||||
$pgpheader = "X-PGP-Sig";
|
$pgpheader = "X-PGP-Sig";
|
||||||
$pgplock = (getpwuid($<))[7] . '/.pgp/config.txt';
|
$pgplock = (getpwuid($<))[7] . '/.pgp/config.txt';
|
||||||
|
|
||||||
# this program is strict about always wanting to be consistent about
|
# this program is strict about always wanting to be consistent about what
|
||||||
# what headers appear in the control messages. the defaults for the
|
# headers appear in the control messages. the defaults for the
|
||||||
# @... arrays are reasonable, but you should edit the force values.
|
# @... arrays are reasonable, but you should edit the force values.
|
||||||
|
|
||||||
# these headers are acceptable in input, but they will be overwritten
|
# these headers are acceptable in input, but they will be overwritten with
|
||||||
# with these values. no sanity checking is done on what you put here.
|
# these values. no sanity checking is done on what you put here. also,
|
||||||
# also, Subject: is forced to be the Control header prepending by "cmsg".
|
# Subject: is forced to be the Control header prepending by "cmsg". also,
|
||||||
# also, Newsgroups: is forced to be just the group being added/removed.
|
# Newsgroups: is forced to be just the group being added/removed.
|
||||||
# (but is taken as-is for checkgroups)
|
# (but is taken as-is for checkgroups)
|
||||||
$force{'Path'} = 'bounce-back';
|
$force{'Path'} = 'bounce-back';
|
||||||
$force{'From'} = 'YOUR_ADDRESS_AND_NAME';
|
$force{'From'} = 'YOUR_ADDRESS_AND_NAME';
|
||||||
|
@ -63,27 +82,27 @@ $force{'X-Info'}='ftp://ftp.isc.org/pub/pgpcontrol/README.html'
|
||||||
. 'ftp://ftp.isc.org/pub/pgpcontrol/README';
|
. 'ftp://ftp.isc.org/pub/pgpcontrol/README';
|
||||||
|
|
||||||
# these headers are acceptable in input, or if not present then will be
|
# these headers are acceptable in input, or if not present then will be
|
||||||
# created with the given value. None are enabled by default, because
|
# created with the given value. None are enabled by default, because they
|
||||||
# they should not be necessary. Setting one to a null string will pass
|
# should not be necessary. Setting one to a null string will pass through
|
||||||
# through any instance of it found in the input, but not generate one
|
# any instance of it found in the input, but not generate one if it is
|
||||||
# if it is missing. If you set any $default{} variables, you must also
|
# missing. If you set any $default{} variables, you must also put it in
|
||||||
# put it in @orderheaders below.
|
# @orderheaders below.
|
||||||
#
|
#
|
||||||
# Note that Distribution nearly never works correctly, so use it only if you
|
# Note that Distribution nearly never works correctly, so use it only if
|
||||||
# are really sure the propagation of the article will be limited as you intend.
|
# you are really sure the propagation of the article will be limited as
|
||||||
# This normally means that you control all servers the distribution will go
|
# you intend. This normally means that you control all servers the
|
||||||
# to with an iron fist.
|
# distribution will go to with an iron fist.
|
||||||
#
|
#
|
||||||
# $use_or_add{'Reply-To'} = 'YOUR_REPLY_ADDRESS';
|
# $use_or_add{'Reply-To'} = 'YOUR_REPLY_ADDRESS';
|
||||||
# $use_or_add{'Oranization'} = 'YOUR_ORGANIZATION';
|
# $use_or_add{'Oranization'} = 'YOUR_ORGANIZATION';
|
||||||
# $use_or_add{'Distribution'} = 'MESSAGE_DISTRIBUTION';
|
# $use_or_add{'Distribution'} = 'MESSAGE_DISTRIBUTION';
|
||||||
|
|
||||||
# host for message-id; this could be determined automatically based on where
|
# host for message-id; this could be determined automatically based on
|
||||||
# it is run, but consistency is the goal here
|
# where it is run, but consistency is the goal here
|
||||||
$id_host = 'FULL_HOST_NAME';
|
$id_host = 'FULL_HOST_NAME';
|
||||||
|
|
||||||
# headers to sign. Sender is included because non-PGP authentication uses it.
|
# headers to sign. Sender is included because non-PGP authentication uses
|
||||||
# The following should always be signed:
|
# it. The following should always be signed:
|
||||||
# Subject -- some older news systems use it to identify the control action.
|
# Subject -- some older news systems use it to identify the control action.
|
||||||
# Control -- most news systems use this to determine what to do.
|
# Control -- most news systems use this to determine what to do.
|
||||||
# Message-ID -- guards against replay attacks.
|
# Message-ID -- guards against replay attacks.
|
||||||
|
@ -97,9 +116,9 @@ $id_host = 'FULL_HOST_NAME';
|
||||||
# set to () if you do not want any headers removed.
|
# set to () if you do not want any headers removed.
|
||||||
@ignoreheaders = ('Sender');
|
@ignoreheaders = ('Sender');
|
||||||
|
|
||||||
# headers that will appear in final message, and their order of appearance.
|
# headers that will appear in final message, and their order of
|
||||||
# all _must_ be set, either in input or via the $force{} and $use_or_add{}
|
# appearance. all _must_ be set, either in input or via the $force{} and
|
||||||
# variables above.
|
# $use_or_add{} variables above.
|
||||||
# (exceptions: Date, Lines, Message-ID are computed by this program)
|
# (exceptions: Date, Lines, Message-ID are computed by this program)
|
||||||
# if header is in use_or_add with a null value, it will not appear in output.
|
# if header is in use_or_add with a null value, it will not appear in output.
|
||||||
# several are required by the news article format standard; if you remove
|
# several are required by the news article format standard; if you remove
|
||||||
|
@ -115,8 +134,9 @@ $id_host = 'FULL_HOST_NAME';
|
||||||
('Path', 'From', 'Newsgroups', 'Subject', 'Control', 'Approved',
|
('Path', 'From', 'Newsgroups', 'Subject', 'Control', 'Approved',
|
||||||
'Message-ID', 'Date', 'Lines', 'X-Info', $pgpheader);
|
'Message-ID', 'Date', 'Lines', 'X-Info', $pgpheader);
|
||||||
|
|
||||||
# this program tries to help you out by not letting you sign erroneous names,
|
# this program tries to help you out by not letting you sign erroneous
|
||||||
# especially ones that are so erroneous they run afoul of naming standards.
|
# names, especially ones that are so erroneous they run afoul of naming
|
||||||
|
# standards.
|
||||||
#
|
#
|
||||||
# set to match only hierarchies you will use it on
|
# set to match only hierarchies you will use it on
|
||||||
# include no '|' for a single hierarchy (eg, "$hierarchies = 'uk';").
|
# include no '|' for a single hierarchy (eg, "$hierarchies = 'uk';").
|
||||||
|
@ -141,6 +161,9 @@ $start_component_with_letter = 'MUST';
|
||||||
|
|
||||||
## END CONFIGURATION
|
## END CONFIGURATION
|
||||||
|
|
||||||
|
use Fcntl qw(F_SETFD);
|
||||||
|
use FileHandle;
|
||||||
|
use IPC::Open3 qw(open3);
|
||||||
use POSIX qw(setlocale strftime LC_TIME);
|
use POSIX qw(setlocale strftime LC_TIME);
|
||||||
use Text::Tabs; # to get 'expand' for tabs in checkgroups
|
use Text::Tabs; # to get 'expand' for tabs in checkgroups
|
||||||
|
|
||||||
|
@ -149,8 +172,10 @@ $0 =~ s#^.*/##;
|
||||||
die "Usage: $0 < message\n" if @ARGV > 0;
|
die "Usage: $0 < message\n" if @ARGV > 0;
|
||||||
|
|
||||||
umask(0022); # flock needs a writable file, if we create it
|
umask(0022); # flock needs a writable file, if we create it
|
||||||
|
if ($pgp !~ /gpg$/) {
|
||||||
open(LOCK, ">>$pgplock") || die "$0: open $lock: $!, exiting\n";
|
open(LOCK, ">>$pgplock") || die "$0: open $lock: $!, exiting\n";
|
||||||
flock(LOCK, 2); # block until locked
|
flock(LOCK, 2); # block until locked
|
||||||
|
}
|
||||||
|
|
||||||
&setgrouppat;
|
&setgrouppat;
|
||||||
|
|
||||||
|
@ -173,7 +198,9 @@ if ($die) {
|
||||||
|
|
||||||
&signit;
|
&signit;
|
||||||
|
|
||||||
|
if ($pgp !~ /gpg$/) {
|
||||||
close(LOCK) || warn "$0: close $lock: $!\n";
|
close(LOCK) || warn "$0: close $lock: $!\n";
|
||||||
|
}
|
||||||
exit 0;
|
exit 0;
|
||||||
|
|
||||||
sub
|
sub
|
||||||
|
@ -303,9 +330,10 @@ readbody
|
||||||
$body = $_ = <STDIN>;
|
$body = $_ = <STDIN>;
|
||||||
$header{'Lines'} = $body =~ tr/\n/\n/ if $body;
|
$header{'Lines'} = $body =~ tr/\n/\n/ if $body;
|
||||||
|
|
||||||
# the following tests are based on the structure of a news.announce.newgroups
|
# the following tests are based on the structure of a
|
||||||
# newgroup message; even if you comment out the "first line" test, please
|
# news.announce.newgroups newgroup message; even if you comment out the
|
||||||
# leave the newsgroups line and moderators checks
|
# "first line" test, please leave the newsgroups line and moderators
|
||||||
|
# checks
|
||||||
if ($action eq 'new') {
|
if ($action eq 'new') {
|
||||||
$status = $moderated ? 'a\smoderated' : 'an\sunmoderated';
|
$status = $moderated ? 'a\smoderated' : 'an\sunmoderated';
|
||||||
$die .= "$0: nonstandard first line in body for $group\n"
|
$die .= "$0: nonstandard first line in body for $group\n"
|
||||||
|
@ -331,8 +359,8 @@ readbody
|
||||||
} else {
|
} else {
|
||||||
$die .= "$0: $group newsgroup line not formatted correctly\n";
|
$die .= "$0: $group newsgroup line not formatted correctly\n";
|
||||||
}
|
}
|
||||||
# moderator checks are disabled; some sites were trying to automatically
|
# moderator checks are disabled; some sites were trying to
|
||||||
# maintain aliases based on this, which is bad policy.
|
# automatically maintain aliases based on this, which is bad policy.
|
||||||
if (0 && $moderated) {
|
if (0 && $moderated) {
|
||||||
$die .= "$0: $group submission address not formatted correctly\n"
|
$die .= "$0: $group submission address not formatted correctly\n"
|
||||||
if $body !~ /\nGroup submission address: ?\S+@\S+\.\S+\n/m;
|
if $body !~ /\nGroup submission address: ?\S+@\S+\.\S+\n/m;
|
||||||
|
@ -357,80 +385,162 @@ readbody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Create a detached signature for the given data. The first argument
|
||||||
|
# should be a key id, the second argument the PGP passphrase (which may be
|
||||||
|
# null, in which case PGP will prompt for it), and the third argument
|
||||||
|
# should be the complete message to sign.
|
||||||
|
#
|
||||||
|
# In a scalar context, the signature is returned as an ASCII-armored block
|
||||||
|
# with embedded newlines. In array context, a list consisting of the
|
||||||
|
# signature and the PGP version number is returned. Returns undef in the
|
||||||
|
# event of an error, and the error text is then stored in @ERROR.
|
||||||
|
#
|
||||||
|
# This function is taken almost verbatim from PGP::Sign except the PGP
|
||||||
|
# style is determined from the name of the program used.
|
||||||
|
sub pgp_sign {
|
||||||
|
my ($keyid, $passphrase, $message) = @_;
|
||||||
|
|
||||||
|
# Ignore SIGPIPE, since we're going to be talking to PGP.
|
||||||
|
local $SIG{PIPE} = 'IGNORE';
|
||||||
|
|
||||||
|
# Determine the PGP style.
|
||||||
|
my $pgpstyle = 'PGP2';
|
||||||
|
if ($pgp =~ /pgps$/) { $pgpstyle = 'PGP5' }
|
||||||
|
elsif ($pgp =~ /gpg$/) { $pgpstyle = 'GPG' }
|
||||||
|
|
||||||
|
# Figure out what command line we'll be using. PGP v6 and PGP v2 use
|
||||||
|
# compatible syntaxes for what we're trying to do. PGP v5 would have,
|
||||||
|
# except that the -s option isn't valid when you call pgps. *sigh*
|
||||||
|
my @command;
|
||||||
|
if ($pgpstyle eq 'PGP5') {
|
||||||
|
@command = ($pgp, qw/-baft -u/, $keyid);
|
||||||
|
} elsif ($pgpstyle eq 'GPG') {
|
||||||
|
@command = ($pgp, qw/--detach-sign --armor --textmode -u/, $keyid,
|
||||||
|
qw/--force-v3-sigs --pgp2/);
|
||||||
|
} else {
|
||||||
|
@command = ($pgp, qw/-sbaft -u/, $keyid);
|
||||||
|
}
|
||||||
|
|
||||||
|
# We need to send the password to PGP, but we don't want to use either
|
||||||
|
# the command line or an environment variable, since both may expose us
|
||||||
|
# to snoopers on the system. So we create a pipe, stick the password in
|
||||||
|
# it, and then pass the file descriptor to PGP. PGP wants to know about
|
||||||
|
# this in an environment variable; GPG uses a command-line flag.
|
||||||
|
# 5.005_03 started setting close-on-exec on file handles > $^F, so we
|
||||||
|
# need to clear that here (but ignore errors on platforms where fcntl or
|
||||||
|
# F_SETFD doesn't exist, if any).
|
||||||
|
#
|
||||||
|
# Make sure that the file handles are created outside of the if
|
||||||
|
# statement, since otherwise they leave scope at the end of the if
|
||||||
|
# statement and are automatically closed by Perl.
|
||||||
|
my $passfh = new FileHandle;
|
||||||
|
my $writefh = new FileHandle;
|
||||||
|
local $ENV{PGPPASSFD};
|
||||||
|
if ($passphrase) {
|
||||||
|
pipe ($passfh, $writefh);
|
||||||
|
eval { fcntl ($passfh, F_SETFD, 0) };
|
||||||
|
print $writefh $passphrase;
|
||||||
|
close $writefh;
|
||||||
|
if ($pgpstyle eq 'GPG') {
|
||||||
|
push (@command, '--batch', '--passphrase-fd', $passfh->fileno);
|
||||||
|
} else {
|
||||||
|
push (@command, '+batchmode');
|
||||||
|
$ENV{PGPPASSFD} = $passfh->fileno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fork off a pgp process that we're going to be feeding data to, and tell
|
||||||
|
# it to just generate a signature using the given key id and pass phrase.
|
||||||
|
my $pgp = new FileHandle;
|
||||||
|
my $signature = new FileHandle;
|
||||||
|
my $errors = new FileHandle;
|
||||||
|
my $pid = eval { open3 ($pgp, $signature, $errors, @command) };
|
||||||
|
if ($@) {
|
||||||
|
@ERROR = ($@, "Execution of $command[0] failed.\n");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write the message to the PGP process. Strip all trailing whitespace
|
||||||
|
# for compatibility with older pgpverify and attached signature
|
||||||
|
# verification.
|
||||||
|
$message =~ s/[ \t]+\n/\n/g;
|
||||||
|
print $pgp $message;
|
||||||
|
|
||||||
|
# All done. Close the pipe to PGP, clean up, and see if we succeeded.
|
||||||
|
# If not, save the error output and return undef.
|
||||||
|
close $pgp;
|
||||||
|
local $/ = "\n";
|
||||||
|
my @errors = <$errors>;
|
||||||
|
my @signature = <$signature>;
|
||||||
|
close $signature;
|
||||||
|
close $errors;
|
||||||
|
close $passfh if $passphrase;
|
||||||
|
waitpid ($pid, 0);
|
||||||
|
if ($? != 0) {
|
||||||
|
@ERROR = (@errors, "$command[0] returned exit status $?\n");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Now, clean up the returned signature and return it, along with the
|
||||||
|
# version number if desired. PGP v2 calls this a PGP MESSAGE, whereas
|
||||||
|
# PGP v5 and v6 and GPG both (more correctly) call it a PGP SIGNATURE,
|
||||||
|
# so accept either.
|
||||||
|
while ((shift @signature) !~ /-----BEGIN PGP \S+-----\n/) {
|
||||||
|
unless (@signature) {
|
||||||
|
@ERROR = ("No signature from PGP (command not found?)\n");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
my $version;
|
||||||
|
while ($signature[0] ne "\n" && @signature) {
|
||||||
|
$version = $1 if ((shift @signature) =~ /^Version:\s+(.*?)\s*$/);
|
||||||
|
}
|
||||||
|
shift @signature;
|
||||||
|
pop @signature;
|
||||||
|
$signature = join ('', @signature);
|
||||||
|
chomp $signature;
|
||||||
|
undef @ERROR;
|
||||||
|
return wantarray ? ($signature, $version) : $signature;
|
||||||
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
signit
|
signit
|
||||||
|
|
||||||
{
|
{
|
||||||
my($tmp, $head, $header, $signheaders, $pgpflags, $pgpbegin, $pgpend);
|
my($head, $header, $signheaders, $pgpflags, $pgpbegin, $pgpend);
|
||||||
local(*FH);
|
|
||||||
|
|
||||||
$tmp = "/tmp/sign-$$";
|
# Form the message to be signed.
|
||||||
$signheaders = join(",", @signheaders);
|
$signheaders = join(",", @signheaders);
|
||||||
|
|
||||||
$head = "X-Signed-Headers: $signheaders\n";
|
$head = "X-Signed-Headers: $signheaders\n";
|
||||||
foreach $header (@signheaders) {
|
foreach $header (@signheaders) {
|
||||||
$head .= "$header: $header{$header}\n";
|
$head .= "$header: $header{$header}\n";
|
||||||
}
|
}
|
||||||
open(FH, "> $tmp") || die "$0: open $tmp: !$\n";
|
my $message = "$head\n$body";
|
||||||
print FH $head;
|
|
||||||
print FH "\n";
|
|
||||||
print FH $body;
|
|
||||||
close(FH) || warn "$0: close TMP: $!\n";
|
|
||||||
die "$0: data to sign corrupted in $tmp\n"
|
|
||||||
if -s $tmp != length($head) + length($body) + 1;
|
|
||||||
|
|
||||||
|
# Get the passphrase if available.
|
||||||
|
my $passphrase;
|
||||||
if ($pgppassfile && -f $pgppassfile) {
|
if ($pgppassfile && -f $pgppassfile) {
|
||||||
$pgppassfile =~ s%^(\s)%./$1%;
|
$pgppassfile =~ s%^(\s)%./$1%;
|
||||||
if (open (PGPPASS, "< $pgppassfile\0")) {
|
if (open (PGPPASS, "< $pgppassfile\0")) {
|
||||||
$ENV{'PGPPASS'} = <PGPPASS>;
|
$passphrase = <PGPPASS>;
|
||||||
close(PGPPASS);
|
close PGPPASS;
|
||||||
|
chomp $passphrase;
|
||||||
}
|
}
|
||||||
chomp $ENV{'PGPPASS'} if $ENV{'PGPPASS'};
|
|
||||||
# if PGPPASS is not now set and non-null, it will be prompted for by pgp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# -f = write to stdout
|
# Sign the message, getting the signature and PGP version number.
|
||||||
# -a = ciphertext file in ASCII radix-64 format
|
my ($signature, $version) = pgp_sign ($pgpsigner, $passphrase, $message);
|
||||||
# -s = sign the plaintext file
|
unless ($signature) {
|
||||||
# -t = plaintext file contains ASCII text, not binary, and
|
die "@ERROR\n$0: could not generate signature\n";
|
||||||
# should be converted to recipient's local text line conventions
|
|
||||||
# -u = userid to use to sign
|
|
||||||
# +verbose=0 = only print errors on stderr
|
|
||||||
open(FH, "exec $pgp -fast -u $pgpsigner +verbose=0 < $tmp 2>/dev/null |") ||
|
|
||||||
die "$0: open pipe from pgp: $!\n";
|
|
||||||
|
|
||||||
# whack any passphrase out of our environment. don't let this kid
|
|
||||||
# you, it does not stop snooping.
|
|
||||||
delete $ENV{'PGPPASS'};
|
|
||||||
|
|
||||||
$pgpbegin = "-----BEGIN PGP SIGNATURE-----";
|
|
||||||
$/ = "$pgpbegin\n";
|
|
||||||
$_ = <FH>; # read to signature, discard
|
|
||||||
die "$0: $pgpbegin not found in $_\n" unless /\Q$pgpbegin\E$/;
|
|
||||||
|
|
||||||
# make sure the temp file goes away even if we die now
|
|
||||||
unlink($tmp) || warn "$0: unlink $tmp: $!\n";
|
|
||||||
|
|
||||||
# finish getting the signature
|
|
||||||
$/ = "\n";
|
|
||||||
$_ = <FH>;
|
|
||||||
die "$0: didn't find PGP Version line where expected\n"
|
|
||||||
unless /^Version: (\S+)$/;
|
|
||||||
$header{$pgpheader} = "$1 $signheaders";
|
|
||||||
|
|
||||||
do { # skip other pgp headers like "charset:" until empty line
|
|
||||||
$_ = <FH>; # ... is charset significant to this application?
|
|
||||||
} while ! /^$/;
|
|
||||||
|
|
||||||
$pgpend = '-----END PGP SIGNATURE-----';
|
|
||||||
while (<FH>) {
|
|
||||||
chomp;
|
|
||||||
last if /^\Q$pgpend\E$/;
|
|
||||||
$header{$pgpheader} .= "\n\t$_";
|
|
||||||
}
|
}
|
||||||
$_ = <FH>;
|
|
||||||
die "$0: unexpected data following $pgpend\n" unless eof(FH);
|
# GnuPG has version numbers containing spaces, which breaks our header
|
||||||
close(FH) || warn "$0: close pipe from pgp returned status $?\n";
|
# format. Find just some portion that contains a digit.
|
||||||
|
($version) = ($version =~ /(\S*\d\S*)/);
|
||||||
|
|
||||||
|
# Put the signature into the headers.
|
||||||
|
$signature =~ s/^/\t/mg;
|
||||||
|
$header{$pgpheader} = "$version $signheaders\n$signature";
|
||||||
|
|
||||||
for (@ignoreheaders) {
|
for (@ignoreheaders) {
|
||||||
delete $header{$_} if defined $header{$_};
|
delete $header{$_} if defined $header{$_};
|
||||||
|
@ -451,8 +561,8 @@ signit
|
||||||
print STDOUT $body;
|
print STDOUT $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Our lawyer told me to include the following. The upshot of it is
|
# Our lawyer told me to include the following. The upshot of it is that
|
||||||
# that you can use the software for free as much as you like.
|
# you can use the software for free as much as you like.
|
||||||
|
|
||||||
# Copyright (c) 1996 UUNET Technologies, Inc.
|
# Copyright (c) 1996 UUNET Technologies, Inc.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
|
@ -483,3 +593,8 @@ signit
|
||||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# cperl-indent-level: 2
|
||||||
|
# fill-column: 74
|
||||||
|
# End:
|
||||||
|
|
Loading…
Reference in a new issue