2011/6/28 Gonéri Le Bouder <[email protected]>:
> 2011/6/28 Guillaume Rousse <[email protected]>
>> Le 28/06/2011 11:57, Gonéri Le Bouder a écrit :
>> > 2011/6/28 Guillaume Rousse <[email protected]

> This means we need to rebuilt the perl tree on the =~ 60 arch we already
> have.
> This will be long, say 1 year. That's the reason why I prefer a soft
> transition.
The attached patch restore Net::SSL. The big difference is IO::Socket::SSL check
hostname directly within OpenSSL whereas Net::SSL don't. We have to do this
ourself which is error-prone.
An interesting point with this patch is OpenSSL stuff are loaded only
when needed. This
is more than 10MB memory saved here on my system.

Guillaume, If you have no objection, I will apply it and then backport
it in 2.1.x branch.

tosh-r630:~/fusioninventory/agent (2.2.x+netssl)$perl -Ilib
./t/components/client/ssl.t
1..14
ok 1 - trusted certificate, correct hostname: connection success
(IO::Socket::SSL)
ok 2 - trusted certificate, correct hostname: connection success (Net::SSL)
ok 3 - trusted certificate, alternate hostname: connection success
(IO::Socket::SSL)
ok 4 # skip Alternate hostname is broken with Net::SSL/Crypt::SSLeay
ok 5 - trusted certificate, joker: connection succes (IO::Socket::SSL)
ok 6 - trusted certificate, joker: connection success (Net::SSL)
ok 7 - trusted certificate, wrong hostname: connection failure (IO::Socket::SSL)
ok 8 - trusted certificate, wrong hostname: connection failure (Net::SSL)
ok 9 - trusted certificate, wrong hostname, no check: connection
success (IO::Socket::SSL)
ok 10 - trusted certificate, wrong hostname, no check: connection
success (Net::SSL)
ok 11 - untrusted certificate, correct hostname: connection failure
(IO::Socket::SSL)
ok 12 - untrusted certificate, correct hostname: connection failure (Net::SSL)
ok 13 - untrusted certificate, correct hostname, no check: connection
success (IO::Socket::SSL)
ok 14 - untrusted certificate, correct hostname, no check: connection
success (Net::SSL)

Best regards,
-- 
     Gonéri Le Bouder
From 9b9b172c6be07cc0755ab983cdb339a7d90e98a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gon=C3=A9ri=20Le=20Bouder?= <[email protected]>
Date: Wed, 10 Aug 2011 23:47:26 +0200
Subject: [PATCH] restore Net::SSL+Crypt::SSL support

This option has some limitation what's why IO::Socket::SSL
remains the best option:
  - No alternate hostname support
  - Hostname validation has to be done manually
---
 lib/FusionInventory/Agent/HTTP/Client.pm |  118 +++++++++++++++++++++++------
 t/components/client/ssl.t                |   67 +++++++++++++++--
 2 files changed, 152 insertions(+), 33 deletions(-)

diff --git a/lib/FusionInventory/Agent/HTTP/Client.pm b/lib/FusionInventory/Agent/HTTP/Client.pm
index 0c68bc2..4ff6da4 100644
--- a/lib/FusionInventory/Agent/HTTP/Client.pm
+++ b/lib/FusionInventory/Agent/HTTP/Client.pm
@@ -20,14 +20,21 @@ sub new {
         if $params{ca_cert_dir} && ! -d $params{ca_cert_dir};
 
     my $self = {
-        logger         => $params{logger} ||
-                          FusionInventory::Agent::Logger->new(),
-        user           => $params{user},
-        password       => $params{password},
-        timeout        => $params{timeout} || 180
+        logger           => $params{logger} ||
+                           FusionInventory::Agent::Logger->new(),
+        user             => $params{user},
+        password         => $params{password},
+        timeout          => $params{timeout} || 180,
+
+        no_ssl_check     => $params{'no_ssl_check'},
+        ssl_socket_class => $params{ssl_socket_class} || "IO::Socket::SSL",
+
+        ca_cert_file     => $params{ca_cert_file},
+        ca_cert_dir      => $params{ca_cert_dir},
     };
     bless $self, $class;
 
+#    $Net::HTTPS::SSL_SOCKET_CLASS = $self->{ssl_socket_class};
     # create user agent
     $self->{ua} = LWP::UserAgent->new(keep_alive => 1, requests_redirectable => ['POST', 'GET', 'HEAD']);
 
@@ -37,32 +44,39 @@ sub new {
         $self->{ua}->env_proxy;
     }
 
+
+    $self->{ua}->agent($FusionInventory::Agent::AGENT_STRING);
+    $self->{ua}->timeout($params{timeout});
+
+    return $self;
+}
+
+sub _turnSSLOn {
+    my ($self) = @_;
+
+    # Already loaded?
+    return if $self->{ssl_on};
+
+    $ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS}=$self->{ssl_socket_class};
     # SSL handling
-    if ($params{'no_ssl_check'}) {
+    if ($self->{'no_ssl_check'}) {
         if ($LWP::VERSION >= 6) {
             # LWP6 default behavior is to check the SSL hostname
             $self->{ua}->ssl_opts(verify_hostname => 0);
         }
-    } else {
-        # only IO::Socket::SSL can perform full server certificate validation,
-        # Net::SSL is only able to check certification authority, and not
-        # certificate hostname
-        IO::Socket::SSL->require();
-        die
-            "failed to load IO::Socket::SSL" .
-            ", unable to perform SSL certificate validation"
-            if $EVAL_ERROR;
+    } elsif ($self->{ssl_socket_class} ne 'Net::SSL' && IO::Socket::SSL->require() && !$EVAL_ERROR) {
+        $self->{ssl_socket_class} = "IO::Socket::SSL";
 
         if ($LWP::VERSION >= 6) {
-            $self->{ua}->ssl_opts(SSL_ca_file => $params{'ca_cert_file'})
-                if $params{'ca_cert_file'};
-            $self->{ua}->ssl_opts(SSL_ca_path => $params{'ca_cert_dir'})
-                if $params{'ca_cert_dir'};
+            $self->{ua}->ssl_opts(SSL_ca_file => $self->{'ca_cert_file'})
+                if $self->{'ca_cert_file'};
+            $self->{ua}->ssl_opts(SSL_ca_path => $self->{'ca_cert_dir'})
+                if $self->{'ca_cert_dir'};
         } else {
             # use a custom HTTPS handler to workaround default LWP5 behaviour
             FusionInventory::Agent::HTTP::Protocol::https->use(
-                ca_cert_file => $params{'ca_cert_file'},
-                ca_cert_dir  => $params{'ca_cert_dir'},
+                ca_cert_file => $self->{'ca_cert_file'},
+                ca_cert_dir  => $self->{'ca_cert_dir'},
             );
             die 
                 "failed to load FusionInventory::Agent::HTTP::Protocol::https" .
@@ -75,14 +89,48 @@ sub new {
 
             # abuse user agent internal to pass values to the handler, so
             # as to have different behaviors in the same process
-            $self->{ua}->{ssl_check} = $params{'no_ssl_check'} ? 0 : 1;
+            $self->{ua}->{ssl_check} = $self->{'no_ssl_check'} ? 0 : 1;
         }
+    } elsif (Crypt::SSLeay->require() && !$EVAL_ERROR) {
+        # This option has some limitation what's why IO::Socket::SSL
+        # remains the best option:
+        #  - No alternate hostname support
+        #  - Hostname validation has to be done manually
+        $self->{ssl_socket_class} = "Net::SSL";
+        if ($LWP::VERSION >= 6) {
+            $self->{ua}->ssl_opts(SSL_ca_file => $self->{'ca_cert_file'})
+	        if $self->{'ca_cert_file'};
+            $self->{ua}->ssl_opts(SSL_ca_path => $self->{'ca_cert_dir'})
+                if $self->{'ca_cert_dir'};
+            # Net::SSL+Crypt::SSLeay are not able to turn OpenSSL internal
+            # hostname check. We check that ourself with If-SSL-Cert-Subject
+            # after. For the moment we turn verify_hostname off
+            $self->{ua}->ssl_opts(verify_hostname => 0);
+            $self->{ua}->ssl_opts(SSL_verify_mode => 1);
+#            $self->{ua}->ssl_opts(SSL_verifycn_scheme => 'www');
+
+	}
+        $ENV{HTTPS_CA_FILE} = $self->{'ca_cert_file'}
+            if $self->{'ca_cert_file'};
+        $ENV{HTTPS_CA_DIR} = $self->{'ca_cert_dir'}
+            if $self->{'ca_cert_dir'};
+
+
+        # abuse user agent internal to pass values to the handler, so
+        # as to have different behaviors in the same process
+        $self->{ua}->{ssl_check} = $self->{'no_ssl_check'} ? 0 : 1;
+
+
+    } else {
+        die
+            "failed to load IO::Socket::SSL or Crypt::SSLeay" .
+            ", unable to perform SSL certificate validation"
     }
 
-    $self->{ua}->agent($FusionInventory::Agent::AGENT_STRING);
-    $self->{ua}->timeout($params{timeout});
+    $self->{logger}->debug("SSL: ".$self->{ssl_socket_class}." loaded");
+
+    $self->{ssl_on} = 1;
 
-    return $self;
 }
 
 sub request {
@@ -94,6 +142,26 @@ sub request {
     my $url = $request->uri();
     my $scheme = $url->scheme();
 
+    if ($scheme eq 'https') {
+        $self->_turnSSLOn();
+# Restore the initial socket class. Needed by the test-suite
+#        $Net::HTTPS::SSL_SOCKET_CLASS = $self->{ssl_socket_class};
+        $ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS}=$self->{ssl_socket_class};
+        $self->{ua}->default_header('');
+#        $self->{ua}->ssl_opts(SSL_verifycn_scheme => undef);
+        if ( (!$self->{no_ssl_check}) && $self->{ssl_socket_class} eq 'Net::SSL' && $url =~ /^https:\/\/([^\/]+).*$/i ) {
+            my $re = $1;
+# Accept SSL cert will hostname with wild-card
+# http://forge.fusioninventory.org/issues/542
+            $re =~ s/^([^\.]+)/($1|\\*)/;
+# protect some characters, $re will be evaluated as a regex
+            $re =~ s/([\-\.])/\\$1/g;
+# Drop the port numbers
+            $re =~ s/:\d+//g;
+            $self->{ua}->default_header('If-SSL-Cert-Subject' => '/CN='.$re.'($|\/)');
+        }
+    }
+
     my $result;
     eval {
         if ($OSNAME eq 'MSWin32' && $scheme eq 'https') {
diff --git a/t/components/client/ssl.t b/t/components/client/ssl.t
index 9851761..ba0e137 100755
--- a/t/components/client/ssl.t
+++ b/t/components/client/ssl.t
@@ -9,13 +9,17 @@ use Socket;
 use Test::More;
 use Test::Exception;
 
+use UNIVERSAL::require;
+
 use FusionInventory::Agent::HTTP::Client;
 use FusionInventory::Test::Server;
 
+my $doNetSSL = Net::SSL->require;
+
 if ($OSNAME eq 'MSWin32' || $OSNAME eq 'darwin') {
     plan skip_all => 'non working test on Windows and MacOS';
 } else {
-    plan tests => 7;
+    plan tests => 8 + ($doNetSSL?6:0);
 }
 
 my $ok = sub {
@@ -43,6 +47,17 @@ my $secure_client = FusionInventory::Agent::HTTP::Client->new(
     logger       => $logger,
     ca_cert_file => 't/ssl/crt/ca.pem',
 );
+my $unsafe_client_net_ssl = FusionInventory::Agent::HTTP::Client->new(
+    logger       => $logger,
+    no_ssl_check => 1,
+    ssl_socket_class => 'Net::SSL'
+) if $doNetSSL;
+my $secure_client_net_ssl = FusionInventory::Agent::HTTP::Client->new(
+    logger       => $logger,
+    ca_cert_file => 't/ssl/crt/ca.pem',
+    ssl_socket_class => 'Net::SSL'
+) if $doNetSSL;
+
 
 # ensure the server get stopped even if an exception is thrown
 $SIG{__DIE__}  = sub { $server->stop(); };
@@ -64,8 +79,13 @@ $server->background();
 
 ok(
     $secure_client->request(HTTP::Request->new(GET => $url))->is_success(),
-    'trusted certificate, correct hostname: connection success'
+    'trusted certificate, correct hostname: connection success (IO::Socket::SSL)'
 );
+ok(
+    $secure_client_net_ssl->request(HTTP::Request->new(GET => $url))->is_success(),
+    'trusted certificate, correct hostname: connection success (Net::SSL)'
+) if $doNetSSL;
+
 
 $server->stop();
 
@@ -86,8 +106,16 @@ $server->background();
 
 ok(
     $secure_client->request(HTTP::Request->new(GET => $url))->is_success(),
-    'trusted certificate, alternate hostname: connection success'
+    'trusted certificate, alternate hostname: connection success (IO::Socket::SSL)'
 );
+# Alternate hostname is broken with Net::SSL
+SKIP: {
+    skip "Alternate hostname is broken with Net::SSL/Crypt::SSLeay", 1;
+    ok(
+        $secure_client_net_ssl->request(HTTP::Request->new(GET => $url))->is_success(),
+        'trusted certificate, alternate hostname: connection success (Net::SSL)'
+    );
+}
 
 $server->stop();
 
@@ -114,8 +142,14 @@ SKIP: {
         $secure_client->request(
             HTTP::Request->new(GET => 'https://localhost.localdomain:8080/public')
         )->is_success(),
-        'trusted certificate, joker: connection success'
+        'trusted certificate, joker: connection succes (IO::Socket::SSL)'
     );
+    ok(
+        $secure_client_net_ssl->request(
+            HTTP::Request->new(GET => 'https://localhost.localdomain:8080/public')
+        )->is_success(),
+        'trusted certificate, joker: connection success (Net::SSL)'
+    ) if $doNetSSL;
 
     $server->stop();
 }
@@ -137,13 +171,22 @@ $server->background();
 
 ok(
     !$secure_client->request(HTTP::Request->new(GET => $url))->is_success(),
-    'trusted certificate, wrong hostname: connection failure'
+    'trusted certificate, wrong hostname: connection failure (IO::Socket::SSL)'
 );
+ok(
+    !$secure_client_net_ssl->request(HTTP::Request->new(GET => $url))->is_success(),
+    'trusted certificate, wrong hostname: connection failure (Net::SSL)'
+) if $doNetSSL;
 
 ok(
     $unsafe_client->request(HTTP::Request->new(GET => $url))->is_success(),
-    'trusted certificate, wrong hostname, no check: connection success'
+    'trusted certificate, wrong hostname, no check: connection success (IO::Socket::SSL)'
 );
+ok(
+    $unsafe_client_net_ssl->request(HTTP::Request->new(GET => $url))->is_success(),
+    'trusted certificate, wrong hostname, no check: connection success (Net::SSL)'
+) if $doNetSSL;
+
 
 $server->stop();
 
@@ -164,13 +207,21 @@ $server->background();
 
 ok(
     !$secure_client->request(HTTP::Request->new(GET => $url))->is_success(),
-    'untrusted certificate, correct hostname: connection failure'
+    'untrusted certificate, correct hostname: connection failure (IO::Socket::SSL)'
 );
+ok(
+    !$secure_client_net_ssl->request(HTTP::Request->new(GET => $url))->is_success(),
+    'untrusted certificate, correct hostname: connection failure (Net::SSL)'
+) if $doNetSSL;
 
 ok(
     $unsafe_client->request(HTTP::Request->new(GET => $url))->is_success(),
-    'untrusted certificate, correct hostname, no check: connection success'
+    'untrusted certificate, correct hostname, no check: connection success (IO::Socket::SSL)'
 );
+ok(
+    $unsafe_client_net_ssl->request(HTTP::Request->new(GET => $url))->is_success(),
+    'untrusted certificate, correct hostname, no check: connection success (Net::SSL)'
+) if $doNetSSL;
 
 $server->stop();
 
-- 
1.7.5.4

_______________________________________________
Fusioninventory-devel mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/fusioninventory-devel

Répondre à