ikegami / perl-LWP-Protocol-AnyEvent-http

Event loop friendly HTTP and HTTPS backend for Perl's LWP
http://search.cpan.org/dist/LWP-Protocol-AnyEvent-http/
Creative Commons Zero v1.0 Universal
9 stars 6 forks source link

ssl issue #7

Closed ghost closed 11 years ago

ghost commented 11 years ago

I posted the details in a comment to a closed pull request, and there's no way for a non-creator to reopen a ticket. Details are here: https://github.com/ikegami/perl-LWP-Protocol-AnyEvent-http/pull/6#issuecomment-21874053

ikegami commented 11 years ago

Looking into it

a.pl:

#!/usr/bin/env perl
use strict;
use warnings;
use Finance::Bitcoin::API;
use if $ARGV[0], 'LWP::Protocol::AnyEvent::http';

my $rpc = Finance::Bitcoin::API->new(
    endpoint => "https://rpc.blockchain.info/"
);

my $ua = $rpc->jsonrpc->ua;
$ua->set_my_handler(
    response_done => sub { $_[0]->dump(maxlength => 0); return }
);

my $res = $rpc->call(getinfo => ());

Without LWP::Protocol::AnyEvent::http:

>perl a.pl 0
HTTP/1.1 200 OK
Connection: close
Date: Mon, 05 Aug 2013 02:02:15 GMT
Server: nginx/1.3.14
Content-Length: 108
Content-Type: application/json;charset=UTF-8
Client-Date: Mon, 05 Aug 2013 02:02:15 GMT
Client-Peer: 91.223.16.20:443
Client-Response-Num: 1
Client-SSL-Cert-Issuer: /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287
Client-SSL-Cert-Subject: /OU=Domain Control Validated/CN=*.blockchain.info
Client-SSL-Cipher: ECDHE-RSA-AES256-GCM-SHA384
Client-SSL-Socket-Class: IO::Socket::SSL

{"error":{"message":"JSON-RPC method [getinfo] with 0 parameters not found.","code":-32601},"jsonrpc":"2.0"}

With LWP::Protocol::AnyEvent::http:

>perl a.pl 1
596 ssl3_get_server_certificate: certificate verify failed
Client-Date: Mon, 05 Aug 2013 02:02:21 GMT
X-AE-URL: https://rpc.blockchain.info/

(no content)
ikegami commented 11 years ago

For now, you can work around the problem by disabling host verification by adding the following:

$rpc->jsonrpc->ua->ssl_opts(verify_hostname => 0);

This, of course, removes much of the usefulness of using https instead of http.

ikegami commented 11 years ago

I've boiled it down to the following:

use strict;
use warnings;
use feature qw( say );

use AnyEvent         qw( );
use AnyEvent::Handle qw( );
use Data::Dumper     qw( Dumper );
use IO::Socket::SSL  qw( );
use Mozilla::CA      qw( );

sub Dump {
   local $Data::Dumper::Useqq = 1;
   local $Data::Dumper::Terse = 1;
   local $Data::Dumper::Indent = 0;
   return Dumper($_[0]);
}

# ---

my $done = AnyEvent->condvar();

my $handle = AnyEvent::Handle->new(
   connect  => [ 'rpc.blockchain.info', 'https' ],
   tls      => 'connect',
   tls_ctx  => {
      verify          => 1,
      verify_peername => 'http',
      ca_file         => Mozilla::CA::SSL_ca_file(),
   },
   on_eof => sub {
      my $self = shift;
      say "AnyEvent::TLS:   ok";
      $self->destroy;
      $done->send;
   },
   on_error => sub {
      my $self = shift;
      say "AnyEvent::TLS:   ", Dump(\@_);
      $self->destroy;
      $done->send;
   },
);

$handle->push_write("GET / HTTP/1.0\015\012\015\012");

$done->recv();

# ---

my $sock = IO::Socket::SSL->new(
   PeerHost        => 'rpc.blockchain.info',
   PeerPort        => 'https',
   SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER,
   SSL_ca_file     => Mozilla::CA::SSL_ca_file(),
);

say "IO::Socket::SSL: ", $sock ? "ok" : "Failed connect or ssl handshake: $!,$IO::Socket::SSL::SSL_ERROR";

Output:

AnyEvent::TLS:   [1,"ssl3_get_server_certificate: certificate verify failed"]
IO::Socket::SSL: ok

AnyEvent::HTTP uses AnyEvent::TLS, and LWP::Protocol::https uses IO::Socket::SSL.

But both AnyEvent::TLS and IO::Socket::SSL use Net::SSLeay, so it should be resolvable!

ikegami commented 11 years ago

Getting closer.

For AnyEvent::TLS, Net::SSLeay::X509_get_subjectAltNames($cert) returns

0  2
1  'blockchain.info'
2  2
3  'www.blockchain.info'

For IO::Socket::SSL, Net::SSLeay::X509_get_subjectAltNames($cert) returns

0  2
1  '*.blockchain.info'
2  2
3  'blockchain.info'
ikegami commented 11 years ago

I've done as much debugging as I can. As best as I can tell, the problem is entirely within AnyEvent::TLS. I've filed a ticket with AnyEvent::TLS. Closing this issue. (Please note that LWP::Protocol::AnyEvent::http's official bug tracker is here.)

ikegami commented 11 years ago

From AnyEvent::TLS's author:

I have no idea how to determine why the returned values are different.

Me neither, but it unlikely has anything to do with AnyEvent::TLS, but depends solely on the server in question replying with the wrong certificate.

A vague guess would be that maybe SNI is disabled and that confuses the server, but that is still simply a buggy server (and bound to trip a lot of code, as SNI isn't common).

In other words, AnyEvent::TLSD might simply use different/tighter security defaults than IO::Socket::SSL. Maybe the server wants tls1.2 when ae::tls offers sslv3 and then fails to negotiate, or it can't cope with th sslv2 connect etc.

Your best bet is to contact the admin of the buggy server in question and ask them to fix their certificate, or, failing that, disabling verification.