-#!/usr/bin/perl -Tw
+#!/usr/bin/perl -w
-# (c) 2000, 2002 Christian Kurz <shorty@debian.org>,
-# Peter Palfrader <peter@palfrader.org>
+# Copyright (c) 2000, 2002 Christian Kurz <shorty@debian.org>,
+# Copyright (c) 2000, 2002, 2005 Peter Palfrader <peter@palfrader.org>
#
# $Id$
#
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
-# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
-# Keylookup homepage: http://www.palfrader.org/keylookup/
-# CVS, BTS: http://savannah.gnu.org/projects/keylookup/
+# Keylookup is part of pgp-tools:
+# http://pgp-tools.alioth.debian.org/
+# svn://svn.debian.org/pgp-tools/trunk/
+# http://svn.debian.org/wsvn/pgp-tools/trunk/
-delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV', 'PATH'};
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
$|=1; # Always flush buffers
use IPC::Open3;
use Getopt::Long;
-my $version = '2.1 ($Id$)';
-
-# The port to use for keyservers unless given otherwise.
-my $PORT=11371;
-# The default proxy port which is used unless the port is explicitly given
-# in the http_proxy environment variable.
-my $PROXY_PORT=3128;
-
-# Name of the GnuPG binary. The executeable must the in the PATH. This may
-# be overriden using the --gnupg= switch.
-my $GnuPG='gpg';
-# Where to find GnuPG's options file.
-my $GNUPGOPTIONS=(defined $ENV{GNUPGHOME} ? $ENV{GNUPGHOME} : $ENV{'HOME'}.'/.gnupg' ) . '/options';
-# Full path to the dialog and whiptail executeable.
-my $Dialog = '/usr/bin/dialog';
-my $Whiptail = '/usr/bin/whiptail';
+my $version = '3.0 ($Id$)';
# Strings to use in the dialog|whiptail frontend
my $TITLE = 'Import Keys';
my $keyserverWasSetOnCmdLine = 0;
-# Queries a remote keyserver (using a proxy if requested) and returns
-# the keyservers anser. No module is used deliberatly to make this
-# script run on as many systems as possible.
-
-sub fetchIt($$$$) {
- my $server = shift;
- my $port = shift;
- my $requestURL = shift;
- my $honorproxy = shift;
-
- my $result;
- my $remote;
-
- if ($honorproxy && defined $ENV{'http_proxy'}) {
- my $proxyserver;
- my $proxyport;
-
- if ( $ENV{'http_proxy'} =~ m#^http://(\S+):(\d+)/?$# ) {
- $proxyserver = $1;
- $proxyport = $2;
- } elsif ( $ENV{'http_proxy'} =~ m#^http://(\S+)/?$# ) {
- $proxyserver = $1;
- $proxyport = $PROXY_PORT;
- } else {
- die ("Unkown http_proxy format");
- };
-
- $remote = IO::Socket::INET->new(
- Proto => 'tcp',
- PeerAddr => $proxyserver,
- PeerPort => $proxyport
- ) || die ("Cannot connect to prox: $!");
-
- my $URL=sprintf("http://%s:%s%s/", $server, $port, $requestURL);
- printf $remote "GET %s HTTP/1.1\n\n", $URL;
- } else {
- $remote = IO::Socket::INET->new(
- Proto => 'tcp',
- PeerAddr => $server,
- PeerPort => $port
- ) || die ("Cannot connect to keysever: $!");
-
- printf $remote "GET %s HTTP/1.1\nHost: %s\n\n\n", $requestURL, $server;
- };
-
-
- {
- local $/;
- $/=undef;
- $result = <$remote>;
- };
- return $result;
-};
+# Maps algorithm numbers to algorithm types as defined in RFC 2400.
+my %ALGOS = (
+ 1 => 'R', # RSA
+ 2 => 'r', # RSA encrypt only (deprecated)
+ 3 => 's', # RSA sign only (deprecated)
+ 16 => 'g', # ElGamal encrypt only
+ 20 => 'g', # ElGamal sign and encrypt (all OpenPGP implementations cryptographically broken, do not use. no longer part of OpenPGP)
+ 17 => 'D' # DSA
+ );
# getHits receives all options as a parameter, calls fetchIT to
sub getHits($) {
my $options = shift;
- die ("$0: No keyserver given!\n") unless (defined $options->{'keyserver'});
-
- my $result = fetchIt(
- $options->{'keyserver'},
- $options->{'port'},
- sprintf("/pks/lookup?op=index&search=%s", $options->{'search'}),
- $options->{'honor-http-proxy'}
- );
-
- $result =~ s/<.*?>//g;
- $result =~ s/>/>/g;
- $result =~ s/</</g;
- $result =~ s/"/"/g;
- $result =~ s/&/&/g;
-
- my @lines = split (/\r?\n/, $result);
-
- shift @lines;
- shift @lines;
- shift @lines;
- shift @lines;
+ my $pid = open(KID, '-|');
+ defined ($pid) or die ("Cannot fork: $!\n");
+ unless ($pid) {
+ close (STDIN);
+ open (STDIN, "/dev/null") || die ("Cannot open /dev/null as stdin: $!\n");
+ # child
+ my @ops = ('gpg');
+ if ($options->{'keyserver'}) {
+ push @ops, '--keyserver='.$options->{'keyserver'};
+ };
+ push @ops, '--command-fd=0';
+ push @ops, '--batch';
+ push @ops, '--no-tty';
+ push @ops, '--with-colons';
+ push @ops, '--fixed-list-mode';
+ push @ops, '--search';
+ push @ops, @{$options->{'search'}};
+ exec(@ops);
+ die ("Cannot exec GnuPG: $!\n");
+ };
my %keys;
my $currentKey;
- for (@lines) {
- if (/^pub /) {
- m, ^pub # pub at the start of the line
- \s+ # whitespace
- (\d+) # bits 1024
- ([A-Za-z]*) # optional type (R or D usually) D
- / # a slash
- ([0-9A-Za-z]+) # keyid 94C09C7F
- \s+ # whitespace
- (\d+)/(\d+)/(\d+) # date 1999/11/10
- \s+ # whitespace
- (.*) # primary user id Peter Palfrader
- ,x or
- warn ("Unexpected format: $_\n"), next;
-
- $currentKey = { 'bits' => $1,
- 'type' => $2,
- 'keyid' => $3,
- 'year' => $4,
- 'month' => $5,
- 'day' => $6,
- 'uid' => [ $7 ]
+ while (<KID>) {
+ chomp;
+ if ( $_ eq "" ) { next; }
+ my ($type, undef) = split /:/;
+ if ($type eq 'pub') {
+ my ($type, $keyid, $algo, $bits, $created, undef, $revoked) = split /:/;
+ $currentKey = { 'bits' => $bits,
+ 'type' => (defined $ALGOS{$algo} ? $ALGOS{$algo} : '#'.$algo),
+ 'keyid' => $keyid,
+ 'created' => $created,
+ 'revoked' => $revoked,
+ 'uid' => []
};
- $keys{ $3 } = $currentKey;
- } elsif (defined $currentKey) {
- s/^\s+//;
- push @{ $currentKey->{'uid'} }, $_;
+ $keys{ $keyid } = $currentKey;
+ } elsif (defined $currentKey && $type eq 'uid') {
+ my ($type, $name) = split /:/;
+ if ($currentKey->{'revoked'} eq 'r') {
+ $name .= ' [REVOKED]';
+ };
+ push @{ $currentKey->{'uid'} }, $name;
};
};
+ close KID;
+ waitpid $pid, 0;
return \%keys;
};
my $keys = shift;
my @keyargs = ();
- for my $keyid (keys %$keys) {
+ for my $keyid (sort {- ($keys->{$a}->{'created'} <=> $keys->{$b}->{'created'})} keys %$keys) {
for (@{ $keys->{$keyid}->{'uid'} }) {
push @keyargs,
$keys->{$keyid}->{'keyid'},
length() <= $MAX_UID_FIELD_LEN ? $_ : substr($_, 0, $MAX_UID_FIELD_LEN-2) . '..',
'off';
};
+ my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef) = localtime ($keys->{$keyid}->{'created'});
+ push @keyargs, $keys->{$keyid}->{'keyid'}, sprintf( "[created: %s-%s-%s]", $year+1900, $mon+1, $mday ), 'off';
push @keyargs, '-'x8, '-'x40, 'off';
};
pop @keyargs;
my $keys = shift;
my @lines = ();
- for my $keyid (keys %$keys) {
+ for my $keyid (sort {- ($keys->{$a}->{'created'} <=> $keys->{$b}->{'created'})} keys %$keys) {
+ my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef) = localtime ($keys->{$keyid}->{'created'});
push @lines, sprintf( "%s%s/%s %s-%s-%s\n",
$keys->{$keyid}->{'bits'},
$keys->{$keyid}->{'type'},
$keys->{$keyid}->{'keyid'},
- $keys->{$keyid}->{'year'},
- $keys->{$keyid}->{'month'},
- $keys->{$keyid}->{'day'} );
+ $year+1900, $mon+1, $mday );
push @lines, map { ' 'x26 . $_ . "\n" } @{ $keys->{$keyid}->{'uid'} };
push @lines, "\n";
};
my %unique;
my @keys = grep { !$unique{$_}++ }
- grep { /^[0-9A-Fa-f]{8}$/ }
+ # get the keyID; can be 8, 16 or 40 nibbles
+ grep { /^((([a-zA-Z0-9]{24})?[a-zA-Z0-9]{8})?[a-zA-Z0-9]{8})$/ }
map { s/\s//g; $_ } <ERRFH>;
wait;
$frontend = 'dialog' unless (defined $frontend);
if ($frontend eq 'dialog') {
- unless (-x $Dialog) {
- warn("Dialog ($Dialog) not executeable/installed. Falling back to Whiptail\n");
+ unless (`which dialog` && $? == 0) {
+ warn("Dialog not executeable/installed. Falling back to Whiptail\n");
$frontend = 'whiptail';
}
};
if ($frontend eq 'whiptail') {
- unless (-x $Whiptail ) {
- warn("Whiptail ($Whiptail) not executeable/installed. Falling back to plain\n");
+ unless (`which whiptail` && $? == 0 ) {
+ warn("Whiptail not executeable/installed. Falling back to plain\n");
$frontend = 'plain';
}
};
if ( $frontend eq 'dialog' ) {
calcDialogSize;
my @ARGS = (
- $Dialog,
+ 'dialog',
'--backtitle',
$BACKTITLE,
'--separate-output',
} elsif ( $frontend eq 'whiptail' ) {
calcDialogSize;
my @ARGS = (
- $Whiptail,
+ 'whiptail',
'--backtitle',
$BACKTITLE,
'--separate-output',
my $keyids = shift;
my $options = shift;
- my @args = ( $options->{'gnupg'},
- '--keyserver',
- $options->{'keyserver'},
- '--recv-keys');
+ my @args = ('gpg');
+ if ($options->{'keyserver'}) {
+ push @args, '--keyserver='.$options->{'keyserver'};
+ };
+ push @args, '--recv-keys';
for my $keyid (@$keyids) {
# untaint keyids
- my ($cleanid) = $keyid =~ /^([a-zA-Z0-9]{8})$/;
+ my ($cleanid) = $keyid =~ /^((([a-zA-Z0-9]{24})?[a-zA-Z0-9]{8})?[a-zA-Z0-9]{8})$/;
warn ("keyid '$keyid' has unexpected format - skipping\n"), next
unless defined $cleanid;
push @args, $cleanid;
}
-
+
print "Calling GnuPG...\n";
exec (@args) || die "can't exec gnupg: $!\n"; # won't return
};
Options:
--keyserver=<keyserver> Select keyserver
- --port=<port> Use a non standard port
--frontend=<frontend> One of whiptail, dialog or plain
--importall Import all matched keys
- --gnupg=<gnupg> use this gnupg binary
- --honor-http-proxy honor the http_proxy environment varibale
--help print this message
EOF
my %options;
GetOptions( \%options,
'keyserver=s',
- 'port=i',
'frontend=s',
'importall',
- 'gnupg=s',
- 'honor-http-proxy',
'version',
'help') or
&usage(1);
&version(0) if ($options{'version'});
&usage(0) if ($options{'help'} || ( scalar(@ARGV) == 0));
- ## If the keyserver was not given on the command line, lurk into
- ## GnuPG's config file, it might be defined there.
- $keyserverWasSetOnCmdLine = defined $options{'keyserver'};
- unless (defined $options{'keyserver'} &&
- defined $options{'honor-http-proxy'} ) {
- if ( open(GNUPGOPTIONS, $GNUPGOPTIONS) ) {
- my $keyserver = $options{'keyserver'};
- while (<GNUPGOPTIONS>) {
- $options{'keyserver'} = $1 if (/^\s*keyserver\s+(\S+?)[#\s]/i && ! defined $keyserver);
- $options{'honor-http-proxy'} = 1 if /^\s*(keyserver-options\s+)honor-http-proxy\s/i;
- };
- close(GNUPGOPTIONS) || warn("Cannot close $GNUPGOPTIONS: $!\n");
- } else {
- warn ("Cannot open $GNUPGOPTIONS: $!\n");
- };
- };
- $options{'port'} = $PORT unless (defined $options{'port'});
- $options{'gnupg'} = $GnuPG unless (defined $options{'gnupg'});
-
- # Untaint it
- $options{'keyserver'} =~ /(.*)/;
- $options{'keyserver'} = $1;
-
## Take all additional arguments to the program as a search target,
## escape the string for use in URLs.
- $options{'search'} = join ' ', @ARGV;
- $options{'search'} =~ s/ ( [^A-Za-z0-9] )
- / '%' . unpack("H2", $1)
- /xeg;
+ $options{'search'} = \@ARGV;
my $keys = getHits( \%options );
my $keyids;
+ if (scalar keys %$keys == 0) {
+ print "GnuPG did not find any keys matching your search string.\n";
+ exit 0;
+ };
if ($options{'importall'}) {
my @allkeys = keys %$keys;
$keyids = \@allkeys;