X-Git-Url: https://git.sthu.org/?a=blobdiff_plain;f=caff%2Fcaff;h=8421fa87efbd2086bd0ad0911364c592c7abb9f1;hb=a01404114acb7f00c2245ad2c8322cfcebf57563;hp=80bbf98d332bd1a8fdbc216efd89782ba6f7f8d8;hpb=af8dc43fab3680949b67cf7d11aa93f763652522;p=pgp-tools.git diff --git a/caff/caff b/caff/caff index 80bbf98..8421fa8 100755 --- a/caff/caff +++ b/caff/caff @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -# caff -- CA - fire and forget +# caff -- CA - Fire and Forget # $Id$ # # Copyright (c) 2004 Peter Palfrader @@ -29,6 +29,126 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=pod + +=head1 NAME + +caff -- CA - Fire and Forget + +=head1 SYNOPSIS + +=over + +=item B [-u I] I [I ..] + +=back + +=head1 DESCRIPTION + +CA Fire and Forget is a script that helps you in keysigning. It takes a list +of keyids on the command line, fetches them from a keyserver and calls GnuPG so +that you can sign it. It then mails each key to all its email addresses - only +including the one UID that we send to in each mail, pruned from all but self +sigs and sigs done by you. + +=head1 OPTIONS + +=over + +=item B<-u> I + +Select the key that is used for signing, in case you have more than one key. + +=back + +=head1 FILES + +=over + +=item $HOME/.caffrc - configuration file + +=back + +=head1 CONFIGURATION FILE OPTIONS + +The configuration file is a perl script that sets values in the hash B<%CONFIG>. + +Example: + + $CONFIG{'owner'} = 'Peter Palfrader'; + $CONFIG{'email'} = 'peter@palfrader.org'; + +=head2 Valid keys + +=over + +=item B [string] + +Base directory for the files caff stores. Default: B<$HOME/.caff/>. + +=item B [string] + +Your name. B. + +=item B [string] + +Your email address, used in From: lines. B. + +=item B [list of keyids] + +A list of your keys. This is used to determine which signatures to keep +in the pruning step. If you select a key using B<-u> it has to be in +this list. B. + +=item B [seconds] + +Don't export UIDs by default, on which your latest signature is older +than this age. Default: B<24*60*60> (i.e. one day). + +=item B [string] + +Keyserver to download keys from. Default: B. + +=item B [string] + +Path to the GnuPG binary. Default: B. + +=item B [string] + +Path to the GnuPG binary which is used to sign keys. Default: what +B is set to. + +=item B [string] + +Path to the GnuPG binary which is used to split off signatures. This is +needed while the upstream GnuPG is not fixed (there are 2 bugs in the +Debian Bug Tracking System). Default: what B is set to. + +=item B [string] + +Path to your secret keyring. Default: B<$HOME/.gnupg/secring.gpg>. + +=item B [keyid] + +An additional keyid to encrypt messages to. Default: none. + +=item B [boolean] + +If true, then skip the step of fetching keys from the keyserver. +Default: B<0>. + +=item B [boolean] + +If true, then skip the signing step. Default: B<0>. + +=back + +=head1 AUTHOR + +Peter Palfrader + +=cut + use strict; use IO::Handle; use English; @@ -51,7 +171,7 @@ sub load_config() { die "Couldn't parse $config: $EVAL_ERROR\n" if $EVAL_ERROR; }; - die ("caffhome is not defined.\n") unless defined $CONFIG{'caffhome'}; + $CONFIG{'caffhome'}=$ENV{'HOME'}.'/.caff' unless defined $CONFIG{'caffhome'}; die ("owner is not defined.\n") unless defined $CONFIG{'owner'}; die ("email is not defined.\n") unless defined $CONFIG{'email'}; die ("keyid is not defined.\n") unless defined $CONFIG{'keyid'}; @@ -66,6 +186,8 @@ sub load_config() { $CONFIG{'gpg-sign'} = $CONFIG{'gpg'} unless defined $CONFIG{'gpg-sign'}; $CONFIG{'gpg-delsig'} = $CONFIG{'gpg'} unless defined $CONFIG{'gpg-delsig'}; $CONFIG{'secret-keyring'} = $ENV{'HOME'}.'/.gnupg/secring.gpg' unless defined $CONFIG{'secret-keyring'}; + $CONFIG{'no-download'} = 0 unless defined $CONFIG{'no-download'}; + $CONFIG{'no-sign'} = 0 unless defined $CONFIG{'no-sign'}; }; sub notice($) { @@ -336,6 +458,15 @@ $CONFIG{'owner'} $message_entity->stringify(); }; +sub sanitize_uid($) { + my ($uid) = @_; + + my $good_uid = $uid; + $good_uid =~ tr#/:\\#_#; + trace2("[sanitize_uid] changed UID from $uid to $good_uid.\n") if $good_uid ne $uid; + return $good_uid; +}; + my $USER; my @KEYIDS; @@ -359,49 +490,57 @@ for my $keyid (@ARGV) { }; + ############################# # receive keys from keyserver ############################# -my $gpg = GnuPG::Interface->new(); -$gpg->call( $CONFIG{'gpg'} ); -$gpg->options->hash_init( - 'homedir' => $GNUPGHOME, - 'extra_args' => '--keyserver='.$CONFIG{'keyserver'} ); -$gpg->options->meta_interactive( 0 ); -my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); -my $pid = $gpg->recv_keys(handles => $handles, command_args => [ @KEYIDS ]); -my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); -waitpid $pid, 0; - my @keyids_ok; my @keyids_failed; +if ($CONFIG{'no-download'}) { + @keyids_ok = @KEYIDS; +} else { + my $gpg = GnuPG::Interface->new(); + $gpg->call( $CONFIG{'gpg'} ); + $gpg->options->hash_init( + 'homedir' => $GNUPGHOME, + 'extra_args' => '--keyserver='.$CONFIG{'keyserver'} ); + $gpg->options->meta_interactive( 0 ); + my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); + + my $pid = $gpg->recv_keys(handles => $handles, command_args => [ @KEYIDS ]); + my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); + waitpid $pid, 0; + # [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F # [GNUPG:] NODATA 1 # [GNUPG:] NODATA 1 # [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039 -for my $line (split /\n/, $status) { - if ($line =~ /^\[GNUPG:\] IMPORT_OK/) { - push @keyids_ok, shift @KEYIDS; - } elsif ($line =~ /^\[GNUPG:\] NODATA/) { - push @keyids_failed, shift @KEYIDS; - }; -} -die ("Still keys in \@KEYIDS. This should not happen.") if scalar @KEYIDS; -notice ("Import failed for: ". (join ' ', @keyids_failed).".") if scalar @keyids_failed; + for my $line (split /\n/, $status) { + if ($line =~ /^\[GNUPG:\] IMPORT_OK/) { + push @keyids_ok, shift @KEYIDS; + } elsif ($line =~ /^\[GNUPG:\] NODATA/) { + push @keyids_failed, shift @KEYIDS; + }; + } + die ("Still keys in \@KEYIDS. This should not happen.") if scalar @KEYIDS; + notice ("Import failed for: ". (join ' ', @keyids_failed).".") if scalar @keyids_failed; +}; ########### # sign keys ########### -info("Sign the following keys according to your policy..."); -for my $keyid (@keyids_ok) { - my @command; - push @command, $CONFIG{'gpg-sign'}; - push @command, '--local-user', $USER if (defined $USER); - push @command, "--homedir=$GNUPGHOME"; - push @command, '--secret-keyring', $CONFIG{'secret-keyring'}; - push @command, '--sign-key', $keyid; - print join(' ', @command),"\n"; - system (@command); +unless ($CONFIG{'no-sign'}) { + info("Sign the following keys according to your policy..."); + for my $keyid (@keyids_ok) { + my @command; + push @command, $CONFIG{'gpg-sign'}; + push @command, '--local-user', $USER if (defined $USER); + push @command, "--homedir=$GNUPGHOME"; + push @command, '--secret-keyring', $CONFIG{'secret-keyring'}; + push @command, '--sign-key', $keyid; + print join(' ', @command),"\n"; + system (@command); + }; }; ################## @@ -411,14 +550,14 @@ KEYS: for my $keyid (@keyids_ok) { # get key listing ################# - $gpg = GnuPG::Interface->new(); + my $gpg = GnuPG::Interface->new(); $gpg->call( $CONFIG{'gpg'} ); $gpg->options->hash_init( 'homedir' => $GNUPGHOME ); $gpg->options->meta_interactive( 0 ); - ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); + my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] ); - $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $keyid ]); - ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); + my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $keyid ]); + my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; if ($stdout eq '') { warn ("No data from gpg for list-key $keyid\n"); @@ -577,7 +716,7 @@ for my $keyid (@keyids_ok) { my $keydir = "$KEYSBASE/$DATE_STRING"; -d $keydir || mkpath($keydir , 0, 0700) or die ("Cannot create $keydir $!\n"); - my $keyfile = "$keydir/$longkeyid.key.$uid_number.$this_uid_text.asc"; + my $keyfile = "$keydir/$longkeyid.key.$uid_number.".sanitize_uid($this_uid_text).".asc"; open (KEY, ">$keyfile") or die ("Cannot open $keyfile\n"); print KEY $asciikey; close KEY; @@ -595,9 +734,10 @@ for my $keyid (@keyids_ok) { } else { my @attached ; for my $uid (@UIDS) { + trace("UID: $uid->{'text'}\n"); unless ($uid->{'text'} =~ /@/) { my $attach = ask("UID $uid->{'text'} is no email address, attach it to every email sent?", 1); - push @attached, $uid; + push @attached, $uid if $attach; }; }; @@ -611,7 +751,7 @@ for my $keyid (@keyids_ok) { my $mail = send_mail($address, $can_encrypt, $longkeyid, $uid, @attached); my $keydir = "$KEYSBASE/$DATE_STRING"; - my $mailfile = "$keydir/$longkeyid.mail.".$uid->{'serial'}.".".$uid->{'text'}; + my $mailfile = "$keydir/$longkeyid.mail.".$uid->{'serial'}.".".sanitize_uid($uid->{'text'}); open (KEY, ">$mailfile") or die ("Cannot open $mailfile\n"); print KEY $mail; close KEY;