mention cabability and validity in verbose output when not capable/valid
[monkeysphere-validation-agent.git] / gpgkeys_hkpms
index 74abf9a67e1a722e09c5007fc5a8238c70b5e920..75ce01bcb293cb5a66dff56f0470e0084ab8ad8c 100755 (executable)
@@ -19,6 +19,8 @@ use warnings;
   use POSIX;
   use Crypt::Monkeysphere::MSVA::Logger;
   use Crypt::Monkeysphere::MSVA::Client;
+  use Regexp::Common qw /net/;
+  use Module::Load::Conditional;
 
   sub parse_input {
     my $self = shift;
@@ -34,6 +36,39 @@ use warnings;
           my @args = split(/ /, $line);
           my $cmd = shift @args;
           $self->{config}->{lc($cmd)} = join(' ', @args);
+          if (lc($cmd) eq 'option') {
+            my $opt = lc($args[0]);
+            if ($opt eq 'debug') {
+              $self->{logger}->set_log_level('debug');
+            } elsif ($opt eq 'verbose') {
+              $self->{logger}->more_verbose();
+            } elsif ($opt eq 'no-check-cert') {
+              $self->{logger}->log('error', "Received no-check-cert option.  Why are you bothering with hkpms if you aren't checking?\n");
+              $self->{actually_check} = 0;
+            } elsif ($opt eq 'check-cert') {
+              $self->{actually_check} = 1;
+            } elsif ($opt =~ /^http-proxy=(.*)/) {
+              my $hp = $1;
+              if ($hp =~ /^(socks|http|https):\/\/($RE{net}{domain}|$RE{net}{IPv4}):([[:digit:]]+)\/?$/) {
+                if ('socks' eq $1) {
+                  if ( ! Module::Load::Conditional::check_install(module => 'LWP::Protocol::socks')) {
+                    $self->{logger}->log('error', "Requesting a socks proxy for hkpms, but LWP::Protocol::socks is not installed.\nThis will likely fail.\n");
+                  }
+                }
+                $self->{proxy} = sprintf('%s://%s:%s', $1, $2, $3);
+              } else {
+                $self->{logger}->log('error', "Failed to make sense of this http-proxy address: '%s'; ignoring.\n", $hp);
+              }
+            } else {
+              $self->{logger}->log('error', "Received '%s' as an option, but gpgkeys_hkpms does not implement it. Ignoring...\n", $opt);
+            }
+            # FIXME: consider other keyserver-options from gpg(1).
+            # in particular, the following might be interesting:
+            # timeout
+            # include-revoked
+            # include-disabled
+            # ca-cert-file
+          }
         }
       } else {
         push(@{$self->{args}}, $line);
@@ -49,7 +84,7 @@ use warnings;
 
     if (exists $self->{cache}->{$certpem}) {
       ($status, $ret) = @{$self->{cache}->{$certpem}};
-      $self->{logger}->log('verbose', "Found response in cache\n");
+      $self->{logger}->log('debug', "Found response in cache\n");
     } else {
       # use Crypt::Monkeysphere::MSVA::Client if available:
       if (defined($self->{client})) {
@@ -57,6 +92,7 @@ use warnings;
         ($status, $ret) = $self->{client}->query_agent('https', $self->{config}->{host}, 'server', 'x509pem', $certpem, 'never');
       } else {
         use Crypt::Monkeysphere::MSVA;
+        $self->{logger}->log('verbose', "Could not find a running agent (MONKEYSPHERE_VALIDATION_AGENT_SOCKET env var).\nFalling back to in-process certificate checks.\n");
         # If there is no running agent, we might want to be able to fall
         # back here.
 
@@ -78,11 +114,18 @@ use warnings;
       # to get called 3 times by perl for some reason. (see
       # https://bugs.debian.org/606249)
       $self->{cache}->{$certpem} = [ $status, $ret ];
-      $self->{logger}->log('info', "%s\n", $ret->{message})
-        if defined $ret->{message};
+      if (JSON::is_bool($ret->{valid}) && ($ret->{valid} eq 1)) {
+        $self->{logger}->log('verbose', "Monkeysphere HKPMS Certificate validation succeeded:\n  %s\n", $ret->{message});
+      } else {
+        $self->{logger}->log('error', "Monkeysphere HKPMS Certificate validation failed:\n  %s\n", $ret->{message});
+      }
     }
 
-    return (JSON::is_bool($ret->{valid}) && ($ret->{valid} eq 1));
+    if (JSON::is_bool($ret->{valid}) && ($ret->{valid} eq 1)) {
+      return 1;
+    } else {
+      return 0;
+    }
   }
 
   sub query {
@@ -91,14 +134,15 @@ use warnings;
     # FIXME: i'd like to pass this debug argument to IO::Socket::SSL,
     # but i don't know how to do that.
     # i get 'Variable "@iosslargs" will not stay shared' if i try to call
-    # use IO::Socket::SSL 1.35 @iosslargs;
+    # use IO::Socket::SSL 1.37 @iosslargs;
     my @iosslargs = ();
     if ($self->{logger}->get_log_level() >= 4) {
       push @iosslargs, sprintf("debug%d", int($self->{logger}->get_log_level() - 3));
     }
 
-    # earlier versions can fail open, defeating the purpose here.
-    use IO::Socket::SSL 1.35;
+    # versions earlier than 1.35 can fail open: bad news!.
+    # 1.37 lets us set ca_path and ca_file to undef, which is what we want.
+    use IO::Socket::SSL 1.37;
     use Net::SSLeay;
     use LWP::UserAgent;
     use URI;
@@ -106,11 +150,19 @@ use warnings;
     IO::Socket::SSL::set_ctx_defaults(
                                       verify_callback => sub { $self->verify_cert(@_); },
                                       verify_mode => 0x03,
-                                      # this parameter is foolish: http://bugs.debian.org/606243
-                                      ca_path => '.',
+                                      ca_path => undef,
+                                      ca_file => undef,
                                      );
 
-    my $ua = LWP::UserAgent->new();
+    my $ua = LWP::UserAgent::->new();
+
+    if (exists($self->{proxy})) {
+      $self->{logger}->log('verbose', "Using http-proxy: %s\n", $self->{proxy});
+      $ua->proxy([qw(http https)] => $self->{proxy});
+    } else {
+      # if no proxy was explicitly set, use the environment:
+      $ua->env_proxy();
+    }
 
     printf("VERSION 1\nPROGRAM %s gpgkeys_hkpms msva-perl/%s\n",
            $self->{config}->{program},  # this is kind of cheating :/
@@ -120,7 +172,7 @@ use warnings;
     $self->{logger}->log('debug', "command: %s\n", $self->{config}->{command});
     if (lc($self->{config}->{command}) eq 'search') {
       # for COMMAND = SEARCH, we want op=index, and we want to rejoin all args with spaces.
-      my $uri = new URI(sprintf('https://%s/pks/lookup', $self->{config}->{host}));
+      my $uri = URI::->new(sprintf('https://%s/pks/lookup', $self->{config}->{host}));
       my $arg = join(' ', @{$self->{args}});
       $uri->query_form(op => 'index',
                        options => 'mr',
@@ -128,6 +180,7 @@ use warnings;
                       );
       $arg =~ s/\n/ /g ; # swap out newlines for spaces
       printf("\n%s %s BEGIN\n", $self->{config}->{command}, $arg);
+      $self->{logger}->log('debug', "URI: %s\n", $uri);
       my $resp = $ua->get($uri);
       if ($resp->is_success) {
         print($resp->decoded_content);
@@ -138,7 +191,7 @@ use warnings;
       printf("\n%s %s END\n", $self->{config}->{command}, $arg);
     } elsif (lc($self->{config}->{command}) eq 'get') {
       # for COMMAND = GET, we want op=get, and we want to issue each query separately.
-      my $uri = new URI(sprintf('https://%s/pks/lookup', $self->{config}->{host}));
+      my $uri = URI::->new(sprintf('https://%s/pks/lookup', $self->{config}->{host}));
       foreach my $arg (@{$self->{args}}) {
         printf("\n%s %s BEGIN\n", $self->{config}->{command}, $arg);
         $uri->query_form(op => 'get',
@@ -168,7 +221,7 @@ use warnings;
           if ($arg eq sprintf('KEY %s END', $keyid)) {
             $self->{logger}->log('debug', "Found KEY END line with %d lines of data elapsed\n", scalar(@keydata));
             # for sending keys, we want to POST to /pks/add, with a keytext variable.
-            my $uri = new URI(sprintf('https://%s/pks/add', $self->{config}->{host}));
+            my $uri = URI::->new(sprintf('https://%s/pks/add', $self->{config}->{host}));
             my $resp = $ua->post($uri, {keytext => join("\n", @keydata)});
             if ($resp->is_success) {
               printf("\n%s", $resp->decoded_content);
@@ -199,18 +252,20 @@ use warnings;
   sub new {
     my $class = shift;
 
+    my $default_log_level = 'error';
     my $client;
     if (exists($ENV{MONKEYSPHERE_VALIDATION_AGENT_SOCKET})) {
-      $client = Crypt::Monkeysphere::MSVA::Client->new(
-                                                       socket => $ENV{MONKEYSPHERE_VALIDATION_AGENT_SOCKET},
-                                                       log_level => $ENV{MSVA_LOG_LEVEL},
-                                                      );
+      $client = Crypt::Monkeysphere::MSVA::Client::->new(
+                                                         socket => $ENV{MONKEYSPHERE_VALIDATION_AGENT_SOCKET},
+                                                         log_level => $default_log_level,
+                                                        );
     }
     my $self = { config => { },
                  args => [ ],
-                 logger => Crypt::Monkeysphere::MSVA::Logger->new($ENV{MSVA_LOG_LEVEL}),
+                 logger => (defined($client) ? $client->{logger} : Crypt::Monkeysphere::MSVA::Logger::->new($default_log_level)),
                  cache => { },
                  client => $client,
+                 actually_check => 1,
                };
 
     bless ($self, $class);
@@ -220,7 +275,7 @@ use warnings;
 }
 
 
-my $hkpms = Crypt::Monkeysphere::MSVA::HKPMS->new();
+my $hkpms = Crypt::Monkeysphere::MSVA::HKPMS::->new();
 
 my $input = # load gpg instructions from stdin:
   do {