[Pgp-tools-commit] r864 - trunk/caff

Guilhem Moulin guilhem-guest at moszumanska.debian.org
Mon Jul 11 22:36:21 UTC 2016


Author: guilhem-guest
Date: 2016-07-11 22:36:21 +0000 (Mon, 11 Jul 2016)
New Revision: 864

Modified:
   trunk/caff/caff
Log:
caff: Refactor key import.

Modified: trunk/caff/caff
===================================================================
--- trunk/caff/caff	2016-07-11 22:36:18 UTC (rev 863)
+++ trunk/caff/caff	2016-07-11 22:36:21 UTC (rev 864)
@@ -474,7 +474,7 @@
     $color = $color ? 'success' : 'fail' if defined $color;
     print STDERR mycolored("[NOTICE] $line", 'notice', $color), "\n";
 }
-sub info($$) {
+sub info($;$) {
     my ($line,$color) = @_;
     $color = $color ? 'success' : 'fail' if defined $color;
     print STDERR mycolored("[INFO] $line", 'info', $color), "\n";
@@ -1244,14 +1244,14 @@
 # @param keyids         keyids of the OpenPGP keys to import
 # @param src_gnupghome  gnupghome directory where to export the key from
 # @param dst_gnupghome  gnupghome directory where to import the key into
-# @param import_options an array of import-options, see gpg(1)
+# @param die_on_error   whether to die if some of the keyids couldn't be imported
+# @param import_options a list of import-options, see gpg(1)
 #
-# @ In list context, return the list of keyids that couldn't be
-# imported.  Otherwise, croak if any key couldn't be imported.
+# @ return a hash reference mapping each key ID to the list of matching
+#   imported key fingerprint.
 #
-sub import_keys_from_gnupghome($$$@) {
-    my ($keyids, $src_gpghome, $dst_gpghome, @import_options) = @_;
-    my %keyids = map {$_ => 1} @$keyids;
+sub import_keys_from_gnupghome($$$$@) {
+    my ($keyids, $src_gpghome, $dst_gpghome, $die_on_error, @import_options) = @_;
     my $src = $src_gpghome // "your normal GnuPGHOME";
     my $dst = $dst_gpghome // "your normal GnuPGHOME";
 
@@ -1270,47 +1270,71 @@
     my $handles = mkGnuPG_fds( stdin  => $pipe, status => undef ); # import keys from $pipe
     my $iPid = $gpg->import_keys( handles => $handles );
 
-    # inspect the $status FD as data gets out.
-    while (readline $handles->{status}) {
-        if (/^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{40})$/) {
-            my $fpr = $1;
-            my @keys = grep { $fpr =~ /$_$/ } @$keyids;
-            mywarn("Multiple (".($#keys+1).") keys matched $fpr in $src") if $#keys > 0;
-            delete @keyids{@keys};
-        }
-    }
+    my $status = import_loop($handles->{status}, defined $src_gpghome ? 0 : 1, $keyids);
     done_gpg($iPid, $handles);  # import done
-    done_gpg($ePid);            # export done
+    waitpid $ePid => 0;         # export done
 
-    return (keys %keyids) if wantarray; # list context
-    myerror(1, "Couldn't import key(s) ".(join ',', keys %keyids)." from $src") if %keyids;
+    my @failed = grep { !@{$status->{$_}} } keys %$status;
+    if (@failed) {
+        my $msg = "Couldn't import key(s) ".(join ',', @failed)." from $src";
+        $die_on_error ? myerror(1, $msg) : info($msg, 0);
+    }
+    return $status;
 }
 
 ##
-# Import a key file into a specified gnupghome.
+# Import loop.
 #
-# @param keyfile       file containing the keys to import
-# @param dst_gnupghome gnupghome directory where to import the key
+# @param fh                 the status file handle from GnuPG::Interface
+# @param verbose            whether to list the status of each key as it
+#                           is being imported.
+# @param keyids             an array of keyids to be imported
+# @param ignore_unexpected  whether not to print a warning upon receiving
+#                           an unexpected key
 #
-# @return 0 if successful\n
-#         1 if an error occured.
+# @ return a hash reference mapping each key ID to the list of matching
+#   imported key fingerprint.
 #
-sub import_key_files($$) {
-    my ($keyfile, $dst_gpghome) = @_;
-    my $gpg = mkGnuPG( homedir => $dst_gpghome, quiet => 1 );
-    $gpg->options->push_extra_args(qw/--import-options import-local-sigs/) if $CONFIG{'gpg-sign-type'} =~ /l/;
-    my $handles = mkGnuPG_fds( status => undef );
-    my $pid = $gpg->import_keys( handles => $handles, command_args => $keyfile );
+sub import_loop($$$;$) {
+    my ($fh, $verbose, $keyids, $ignore_unexpected) = @_;
 
-    my $err = 1;
-    while (readline $handles->{status}) {
-        if (/^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{40})$/) {
-            info("Key $1 imported from $keyfile", 1);
-            $err = 0;
+    # [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F
+    # [GNUPG:] NODATA 1
+    # [GNUPG:] NODATA 1
+    # [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039
+    my %status = map { $_ => [] } @$keyids;
+    while (<$fh>) {
+        # inspect the $status FD as data gets out
+        if (/^\[GNUPG:\] IMPORT_OK (\d+) ([0-9A-F]{40})$/) {
+            my ($r, $fpr) = ($1, $2);
+            my @matching_keyids = grep { $fpr =~ /\Q$_\E$/ } @$keyids;
+            unless (@matching_keyids) {
+                unless ($ignore_unexpected) {
+                    mywarn("Imported unexpected key $fpr.  Are you trying to work on a subkey?");
+                } elsif ($verbose) {
+                    info( "Key $fpr ". ($r == 0 ? 'not changed' : 'imported'), ($r == 0 ? undef : 1) );
+                }
+                next;
+            }
+            debug( "Imported $fpr for ".join(',', @matching_keyids));
+            info( "Key " .join(',', @matching_keyids).' '. ($r == 0 ? 'not changed' : 'imported'), ($r == 0 ? undef : 1) ) if $verbose;
+            push @{$status{$_}}, $fpr foreach @matching_keyids;
         }
+        elsif (/^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{32})$/) {
+            mywarn("Imported v3 key $1.  Version 3 keys are obsolete, should not be used, and are not and will not be properly supported.");
+        }
+        elsif (!/^\[GNUPG:\]\ (?:NODATA\ \d
+                               | IMPORT_RES\ .+
+                               | IMPORTED\ .+
+                               | KEYEXPIRED\ \d+
+                               | SIGEXPIRED\ (?:\ deprecated-use-keyexpired-instead)?
+                               | KEY_CONSIDERED\ [0-9A-F]{40}\ \d+
+                               | FAILURE\ recv-keys\ \d+
+                               )$/x) {
+            mywarn("Got unknown reply from gpg: ".$_);
+        }
     }
-    done_gpg($pid, $handles);
-    return $err;
+    return \%status;
 }
 
 ##
@@ -1323,19 +1347,59 @@
 #
 sub import_keys_to_sign($) {
     my $keyids = shift;
-    # Check if we can find the gpg key from our normal gnupghome, and then
-    # try to import it into our working gnupghome directory
+    return unless $CONFIG{'keys-from-gnupg'} or @{$CONFIG{'key-files'}} or $CONFIG{'no-download'};
+
+    # map each keyid to a list of matching fingerprints; there is a
+    # collision if a keyid is mapped to multiple fingerprints, but we'll
+    # detect that later in the code
+    my $status = { map { $_ => [] } @$keyids };
+
     if ($CONFIG{'keys-from-gnupg'}) {
-        my @failed = import_keys_from_gnupghome($keyids, undef, $GNUPGHOME);
-        foreach my $keyid (@$keyids) {
-            info("Key $keyid imported from your normal GnuPGHOME", 1)
-                unless grep { $keyid eq $_ } @failed;
-        }
-    };
+        notice("Importing keys from your normal GnuPGHOME (".($ENV{'GNUPGHOME'} // "$ENV{'HOME'}/.gnupg").")");
+        merge_import_status( $status, import_keys_from_gnupghome($keyids, undef, $GNUPGHOME, 0) );
+    }
 
-    # Import user specified key files
-    import_key_files($_, $GNUPGHOME) foreach @{$CONFIG{'key-files'}};
+    foreach my $keyfile (@{$CONFIG{'key-files'}}) {
+        notice("Importing key file $keyfile");
+
+        my $gpg = mkGnuPG( homedir => $GNUPGHOME, quiet => 1 );
+        $gpg->options->push_extra_args(qw/--import-options import-local-sigs/) if $CONFIG{'gpg-sign-type'} =~ /l/;
+        my $handles = mkGnuPG_fds( status => undef );
+        my $pid = $gpg->import_keys( handles => $handles, command_args => $keyfile );
+
+        merge_import_status( $status, import_loop($handles->{status}, 1, $keyids, 1) );
+        done_gpg($pid, $handles);
+    }
+
+    # Receive keys from keyserver
+    unless ($CONFIG{'no-download'}) {
+        notice("Fetching keys from a keyserver (this may take a while)...");
+        my @args = (extra_args => ['--keyserver='.$CONFIG{'keyserver'}]) if defined $CONFIG{'keyserver'};
+        my $gpg = mkGnuPG( homedir => $GNUPGHOME, @args );
+        # logger: requesting key ... from hkp
+        # stdout: gpgkeys: key ... not found on keyserver
+        my $handles = mkGnuPG_fds( status => undef );
+        my $pid = $gpg->recv_keys(handles => $handles, command_args => $keyids);
+
+        my $s = import_loop($handles->{status}, 1, $keyids);
+        merge_import_status($status, $s);
+        done_gpg($pid, $handles);
+
+        my @failed = grep { !@{$s->{$_}} } keys %$s;
+        info("Couldn't import key(s) ".(join ',', @failed)." from the keyserver", 0) if @failed;
+    }
+
+    my @failed = grep { !@{$status->{$_}} } keys %$status;
+    if (@failed) {
+        exit 1 unless ask ("Some keys could not be imported - continue anyway?", 0);
+        mywarn("Assuming ". join(' ', @failed).' '.($#failed > 0 ? 'are' : 'is a')." fine keyid".($#failed > 0 ? 's' : ''));
+    }
 }
+sub merge_import_status($$) {
+    foreach my $keyid (keys %{$_[1]}) {
+        push @{$_[0]->{$keyid}}, @{$_[1]->{$keyid}};
+    }
+}
 
 ##
 # A non-localized version of POSIX::strftime.
@@ -1540,74 +1604,9 @@
 ##################################
 # import own keys and keys to sign
 ##################################
-import_keys_from_gnupghome($CONFIG{'keyid'}, undef, $GNUPGHOME);
+import_keys_from_gnupghome($CONFIG{'keyid'}, undef, $GNUPGHOME, 1);
 import_keys_to_sign(\@KEYIDS);
 
-#############################
-# receive keys from keyserver
-#############################
-unless ($CONFIG{'no-download'}) {
-    notice("Fetching keys from a keyserver this may take a while...");
-    my @args = (extra_args => ['--keyserver='.$CONFIG{'keyserver'}]) if defined $CONFIG{'keyserver'};
-    my $gpg = mkGnuPG( homedir => $GNUPGHOME, @args );
-    # logger: requesting key ... from hkp
-    # stdout: gpgkeys: key ... not found on keyserver
-    my $handles = mkGnuPG_fds( status => undef );
-    my $pid = $gpg->recv_keys(handles => $handles, command_args => \@KEYIDS);
-
-    # [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F
-    # [GNUPG:] NODATA 1
-    # [GNUPG:] NODATA 1
-    # [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039
-    my %local_keyids = map { $_ => 1 } @KEYIDS;
-    my $had_v3_keys = 0;
-    @KEYIDS = ();
-    while (readline $handles->{status}) {
-        if (/^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{40})$/) {
-            my $imported_key = $1;
-            my $whole_fpr = $imported_key;
-            my $long_keyid = substr($imported_key, -16);
-            my $short_keyid = substr($imported_key, -8);
-            my $speced_key;
-            for my $spec (($whole_fpr, $long_keyid, $short_keyid)) {
-                $speced_key = $spec if $local_keyids{$spec};
-            };
-            unless ($speced_key) {
-                mywarn("Imported unexpected key; got: $imported_key\nAre you trying to work on a subkey?");
-                next;
-            };
-            debug ("Imported $imported_key for $speced_key");
-            delete $local_keyids{$speced_key};
-            unshift @KEYIDS, $imported_key;
-        } elsif (/^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{32})$/) {
-            my $imported_key = $1;
-            mywarn("Imported v3 key $1.  Version 3 keys are obsolete, should not be used, and are not and will not be properly supported.");
-            $had_v3_keys = 1;
-        } elsif (!/^\[GNUPG:\]\ (?:NODATA\ \d
-                                 | IMPORT_RES\ .+
-                                 | IMPORTED\ .+
-                                 | KEYEXPIRED\ \d+
-                                 | SIGEXPIRED\ (?:\ deprecated-use-keyexpired-instead)?
-                                 | KEY_CONSIDERED\ [0-9A-F]{40}\ \d+
-                                 | FAILURE\ recv-keys\ \d+
-                                 )$/x) {
-            mywarn("Got unknown reply from gpg: ".$_);
-        }
-    };
-    done_gpg($pid, $handles);
-
-    if (scalar %local_keyids) {
-        mywarn("Import failed for: ". (join ' ', keys %local_keyids)."." . ($had_v3_keys ? " (Or maybe it's one of those ugly v3 keys?)" :  ""));
-        exit 1 unless ask ("Some keys could not be imported - continue anyway?", 0);
-        if (scalar keys %local_keyids == 1) {
-            mywarn("Assuming ". (join ' ', keys %local_keyids)." is a fine keyid");
-        } else {
-            mywarn("Assuming ". (join ' ', keys %local_keyids)." are fine keyids");
-        };
-        push @KEYIDS, keys %local_keyids;
-    }
-}
-
 if ($CONFIG{'ask-sign'} && ! $CONFIG{'no-sign'}) {
     $CONFIG{'no-sign'} = ! ask("Continue with signing?", 1);
 }
@@ -1713,8 +1712,8 @@
     my $keydir = File::Temp->newdir( "caff-$keyid-XXXXX", TMPDIR => 1 );
     # we can't use only one import here because the cleaning is done as the
     # keys come and our keys might not be imported yet
-    import_keys_from_gnupghome($CONFIG{'keyid'}, $GNUPGHOME, $keydir, 'import-minimal', 'import-local-sigs');
-    import_keys_from_gnupghome([$fpr],           $GNUPGHOME, $keydir, 'import-clean',   'import-local-sigs');
+    import_keys_from_gnupghome($CONFIG{'keyid'}, $GNUPGHOME, $keydir, 1, 'import-minimal', 'import-local-sigs');
+    import_keys_from_gnupghome([$fpr],           $GNUPGHOME, $keydir, 1, 'import-clean',   'import-local-sigs');
 
     # the first UID. we won't delete that one when pruning for UATs because a key has to have at least one UID
     my @uids = @{$KEYS{$keyid}->{uids}};
@@ -1831,7 +1830,7 @@
         # import the pruned keys with our own local sigs only; this is
         # required even if there are no lsigs, to ensure we've got all
         # UIDs in our own GnuPGHOME
-        import_keys_from_gnupghome( [$fpr], $keydir, undef, 'import-local-sigs' );
+        import_keys_from_gnupghome( [$fpr], $keydir, undef, 1, 'import-local-sigs' );
     }
     undef $keydir; # delete dir
 




More information about the Pgp-tools-commit mailing list