X-Git-Url: http://git.sthu.org/?a=blobdiff_plain;f=caff%2Fcaff;h=866af1919956fc271ddb86dc45418e4089aaef92;hb=19c5ad0c18bc815c0507ae0f83eb33c7798db932;hp=013ebaa7b7eea92da00e69423384e7f668b0037b;hpb=5f021c393cfd6dabe126a9a6c1e8d19775eda517;p=pgp-tools.git diff --git a/caff/caff b/caff/caff index 013ebaa..866af19 100755 --- a/caff/caff +++ b/caff/caff @@ -85,6 +85,10 @@ Do not sign the keys. Select the key that is used for signing, in case you have more than one key. +=item B<--key-file> I + +Import keys from file. Can be supplied more than once. + =back =head1 FILES @@ -156,7 +160,7 @@ An additional keyid to encrypt messages to. Default: none. =item B [string] -Additional arguments to pass to gpg. Default: none. +Additional arguments to pass to gpg. Default: none. =head2 Keyserver settings @@ -169,12 +173,21 @@ Keyserver to download keys from. Default: B. If true, then skip the step of fetching keys from the keyserver. Default: B<0>. +=item B [list of files] + +A list of files containing keys to be imported. + =head2 Signing settings =item B [boolean] If true, then skip the signing step. Default: B<0>. +=item B [boolean] + +If true, then pause before continuing to the signing step. +This is useful for offline signing. Default: B<0>. + =item B [seconds] Don't export UIDs by default, on which your latest signature is older @@ -266,7 +279,7 @@ sub load_config() { die ("keyid is not defined.\n") unless defined $CONFIG{'keyid'}; die ("keyid is not an array ref\n") unless (ref $CONFIG{'keyid'} eq 'ARRAY'); for my $keyid (@{$CONFIG{'keyid'}}) { - $keyid =~ /^[A-Fa-z0-9]{16}$/ or die ("key $keyid is not a long (16 digit) keyid.\n"); + $keyid =~ /^[A-F0-9]{16}$/i or die ("key $keyid is not a long (16 digit) keyid.\n"); }; @{$CONFIG{'keyid'}} = map { uc } @{$CONFIG{'keyid'}}; $CONFIG{'export-sig-age'}= 24*60*60 unless defined $CONFIG{'export-sig-age'}; @@ -277,6 +290,7 @@ sub load_config() { $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'}; + $CONFIG{'key-files'} = () unless defined $CONFIG{'key-files'}; $CONFIG{'mail-template'} = <<'EOM' unless defined $CONFIG{'mail-template'}; Hi, @@ -285,9 +299,13 @@ please find attached the user id{(scalar @uids >= 2 ? 's' : '')}. $OUT .= "\t".$uid."\n"; };} of your key {$key} signed by me. -Note that I did not upload your key to any keyservers. If you want this -new signature to be available to others, please upload it yourself. -With GnuPG this can be done using +Note that I did not upload your key to any keyservers. +If you have multiple user ids, I sent the signature for each user id +separately to that user id's associated email address. You can import +the signatures by running each through `gpg --import`. + +If you want this new signature to be available to others, please upload +it yourself. With GnuPG this can be done using gpg --keyserver subkeys.pgp.net --send-key {$key} If you have any questions, don't hesitate to ask. @@ -429,16 +447,35 @@ sub readwrite_gpg($$$$$%) { sub ask($$;$$) { my ($question, $default, $forceyes, $forceno) = @_; - return $default if $forceyes and $forceno; - return 1 if $forceyes; - return 0 if $forceno; my $answer; + my $yn = $default ? '[Y/n]' : '[y/N]'; while (1) { - print $question,' ',($default ? '[Y/n]' : '[y/N]'), ' '; + print $question,' ',$yn, ' '; + if ($forceyes && $forceno) { + print "$default (from config/command line)\n"; + return $default; + }; + if ($forceyes) { + print "YES (from config/command line)\n"; + return 1; + }; + if ($forceno) { + print "NO (from config/command line)\n"; + return 0; + }; + $answer = ; + if (!defined $answer) { + $OUTPUT_AUTOFLUSH = 1; + die "\n\n". + "End of STDIN reached. Are you using xargs? Caff wants to read from STDIN,\n". + "so you can't really use it with xargs. A patch against caff to read from\n". + "the terminal would be apprechiated.\n". + "For now instead of cat keys | xargs caff do caff `cat keys`\n"; + }; chomp $answer; - last if ((defined $answer) && (length $answer <= 1)); - print "grrrrrr.\n"; + last if ((length $answer == 0) || ($answer =~ m/^[yYnN]$/) ); + print "What about $yn is so hard to understand?\nAnswer with either 'n' or 'y' or just press enter for the default.\n"; sleep 1; }; my $result = $default; @@ -492,9 +529,16 @@ sub export_key($$) { my $gpg = GnuPG::Interface->new(); $gpg->call( $CONFIG{'gpg'} ); - $gpg->options->hash_init( - 'homedir' => $gnupghome, - 'armor' => 1 ); + if (defined $gnupghome) { + $gpg->options->hash_init( + 'homedir' => $gnupghome, + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always } ], + 'armor' => 1 ); + } else { + $gpg->options->hash_init( + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always } ], + 'armor' => 1 ); + }; $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); my $pid = $gpg->export_keys(handles => $handles, command_args => [ $keyid ]); @@ -512,7 +556,9 @@ sub import_key($$) { my $gpg = GnuPG::Interface->new(); $gpg->call( $CONFIG{'gpg'} ); - $gpg->options->hash_init( 'homedir' => $gnupghome ); + $gpg->options->hash_init( + 'homedir' => $gnupghome, + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always } ] ); $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); my $pid = $gpg->import_keys(handles => $handles); @@ -560,9 +606,9 @@ sub send_mail($$$@) { Type => "application/pgp-keys", Disposition => 'attachment', Encoding => "7bit", - Description => "PGP Key 0x$key_id, uid ".($key->{'text'}).' ('.($key->{'serial'}).')', + Description => "PGP Key 0x$key_id, uid ".($key->{'text'}).' ('.($key->{'serial'}).'), signed by 0x'.$CONFIG{'keyid'}[0], Data => $key->{'key'}, - Filename => "0x$key_id.".$key->{'serial'}.".asc"); + Filename => "0x$key_id.".$key->{'serial'}.".signed-by-0x".$CONFIG{'keyid'}[0].".asc"); }; if ($can_encrypt) { @@ -571,7 +617,7 @@ sub send_mail($$$@) { my $gpg = GnuPG::Interface->new(); $gpg->call( $CONFIG{'gpg'} ); $gpg->options->hash_init( 'homedir' => $GNUPGHOME, - 'extra_args' => '--always-trust', + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always } ], 'armor' => 1 ); $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); @@ -689,6 +735,7 @@ if (!GetOptions ( '--no-download' => \$params->{'no-download'}, '-S' => \$params->{'no-sign'}, '--no-sign' => \$params->{'no-sign'}, + '--key-file=s@' => \$params->{'key-files'}, )) { usage(\*STDERR, 1); }; @@ -706,7 +753,7 @@ usage(\*STDERR, 1) unless scalar @ARGV >= 1; if ($params->{'local-user'}) { $USER = $params->{'local-user'}; $USER =~ s/^0x//i; - unless ($USER =~ /^([A-Z0-9]{8}|[A-Z0-9]{16}|[A-Z0-9]{40})$/i) { + unless ($USER =~ /^([A-F0-9]{8}|[A-F0-9]{16}|[A-F0-9]{40})$/i) { print STDERR "-u $USER is not a keyid.\n"; usage(\*STDERR, 1); }; @@ -715,7 +762,11 @@ if ($params->{'local-user'}) { for my $keyid (@ARGV) { $keyid =~ s/^0x//i; - unless ($keyid =~ /^([A-Z0-9]{8}|[A-Z0-9]{16}||[A-Z0-9]{40})$/i) { + unless ($keyid =~ /^([A-F0-9]{8}|[A-F0-9]{16}||[A-F0-9]{40})$/i) { + if ($keyid =~ /^[A-F0-9]{32}$/) { + info("Ignoring v3 fingerprint $keyid. v3 keys are obsolete."); + next; + }; print STDERR "$keyid is not a keyid.\n"; usage(\*STDERR, 1); }; @@ -726,32 +777,59 @@ $CONFIG{'no-download'} = $params->{'no-download'} if defined $params->{'no-downl $CONFIG{'no-mail'} = $params->{'no-mail'} if defined $params->{'no-mail'}; $CONFIG{'mail'} = $params->{'mail'} if defined $params->{'mail'}; $CONFIG{'no-sign'} = $params->{'no-sign'} if defined $params->{'no-sign'}; +push @{$CONFIG{'key-files'}}, @{$params->{'key-files'}} if defined $params->{'key-files'}; ################# # import own keys ################# +for my $keyid (@{$CONFIG{'keyid'}}) { my $gpg = GnuPG::Interface->new(); $gpg->call( $CONFIG{'gpg'} ); $gpg->options->hash_init( 'homedir' => $GNUPGHOME, - 'extra_args' => '--keyserver='.$CONFIG{'keyserver'} ); + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always --with-colons --fixed-list-mode --fast-list-mode } ] ); $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); - $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] ); - my $pid = $gpg->list_public_keys(handles => $handles, command_args => $CONFIG{'keyid'}); + 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\n"); - next; + warn ("No data from gpg for list-key\n"); # There should be at least 'tru:' everywhere. }; - foreach my $keyid (@{$CONFIG{'keyid'}}) { - unless ($stdout =~ /^pub:(?:[^:]*:){3,3}$keyid:/m) { - info("Importing $keyid"); - system "gpg --export $keyid | gpg --import --homedir $GNUPGHOME"; - } + unless ($stdout =~ /^pub:(?:[^:]*:){3,3}$keyid:/m) { + info("Key $keyid not found in caff's home. Getting it from your normal GnuPGHome."); + my $key = export_key(undef, $keyid); + if (!defined $key || $key eq '') { + warn ("Did not get key $keyid from your normal GnuPGHome\n"); + next; + }; + my $result = import_key($GNUPGHOME, $key); + unless ($result) { + warn ("Could not import $keyid into caff's gnupghome.\n"); + next; + }; } +} + +######################## +# import keys from files +######################## +foreach my $keyfile (@{$CONFIG{'key-files'}}) { + my $gpg = GnuPG::Interface->new(); + $gpg->call( $CONFIG{'gpg'} ); + $gpg->options->hash_init('homedir' => $GNUPGHOME); + $gpg->options->meta_interactive( 0 ); + my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); + my $pid = $gpg->import_keys(handles => $handles, command_args => $keyfile); + my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); + info ("Importing keys from $keyfile"); + waitpid $pid, 0; + if ($status !~ /^\[GNUPG:\] IMPORT_OK/m) { + warn $stderr; + } +} ############################# # receive keys from keyserver @@ -766,7 +844,7 @@ if ($CONFIG{'no-download'}) { $gpg->call( $CONFIG{'gpg'} ); $gpg->options->hash_init( 'homedir' => $GNUPGHOME, - 'extra_args' => '--keyserver='.$CONFIG{'keyserver'} ); + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always }, '--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 ]); @@ -814,6 +892,10 @@ unless (@keyids_ok) { ########### # sign keys ########### +if ($CONFIG{'ask-sign'} && ! $CONFIG{'no-sign'}) { + $CONFIG{'no-sign'} = ! ask("Continue with signing?", 1); +} + unless ($CONFIG{'no-sign'}) { info("Sign the following keys according to your policy, then exit gpg with 'save' after signing each key"); for my $keyid (@keyids_ok) { @@ -822,10 +904,11 @@ unless ($CONFIG{'no-sign'}) { push @command, '--local-user', $USER if (defined $USER); push @command, "--homedir=$GNUPGHOME"; push @command, '--secret-keyring', $CONFIG{'secret-keyring'}; - push @command, split ' ', $CONFIG{'gpg-sign-args'} || ""; + push @command, '--no-auto-check-trustdb'; + push @command, '--trust-model=always'; push @command, '--edit', $keyid; push @command, 'sign'; - push @command, 'save'; + push @command, split ' ', $CONFIG{'gpg-sign-args'} || ""; print join(' ', @command),"\n"; system (@command); }; @@ -840,10 +923,11 @@ for my $keyid (@keyids_ok) { ################# my $gpg = GnuPG::Interface->new(); $gpg->call( $CONFIG{'gpg'} ); - $gpg->options->hash_init( 'homedir' => $GNUPGHOME ); + $gpg->options->hash_init( + 'homedir' => $GNUPGHOME, + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always --with-colons --fixed-list-mode } ] ); $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); - $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] ); my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $keyid ]); my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; @@ -901,7 +985,7 @@ for my $keyid (@keyids_ok) { $gpg->call( $CONFIG{'gpg-delsig'} ); $gpg->options->hash_init( 'homedir' => $tempdir, - 'extra_args' => [ '--with-colons', '--fixed-list-mode', '--command-fd=0', '--no-tty' ] ); + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always --with-colons --fixed-list-mode --command-fd=0 --no-tty } ] ); ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $pid = $gpg->wrap_call( commands => [ '--edit' ], @@ -1047,7 +1131,7 @@ for my $keyid (@keyids_ok) { if (!$uid->{'is_uat'} && ($uid->{'text'} =~ /@/)) { my $address = $uid->{'text'}; $address =~ s/.*<(.*)>.*/$1/; - if (ask("Send mail to '$address' for $uid->{'text'}?", 1, $CONFIG{'mail'})) { + if (ask("Mail signature for $uid->{'text'} to '$address'?", 1, $CONFIG{'mail'})) { my $mail = send_mail($address, $can_encrypt, $longkeyid, $uid, @attached); my $keydir = "$KEYSBASE/$DATE_STRING";