THRIFT-3272 (THRIFT-4066) fix perl SSL authentication support; fixed error in erlang test client using wrong key file and added CA
Client: Perl
This closes #1189
diff --git a/lib/perl/lib/Thrift/SSLServerSocket.pm b/lib/perl/lib/Thrift/SSLServerSocket.pm
index 4daaf08..e885ede 100644
--- a/lib/perl/lib/Thrift/SSLServerSocket.pm
+++ b/lib/perl/lib/Thrift/SSLServerSocket.pm
@@ -48,21 +48,27 @@
sub __client
{
- return new Thrift::SSLSocket();
+ return new Thrift::SSLSocket();
}
sub __listen
{
my $self = shift;
- return IO::Socket::SSL->new(LocalAddr => $self->{host},
- LocalPort => $self->{port},
- Proto => 'tcp',
- Listen => $self->{queue},
- ReuseAddr => 1,
- SSL_cert_file => $self->{cert},
- SSL_key_file => $self->{key},
- SSL_ca_file => $self->{ca});
-}
+ my $opts = {Listen => $self->{queue},
+ LocalAddr => $self->{host},
+ LocalPort => $self->{port},
+ Proto => 'tcp',
+ ReuseAddr => 1};
+ $opts->{SSL_ca_file} = $self->{ca} if defined $self->{ca};
+ $opts->{SSL_cert_file} = $self->{cert} if defined $self->{cert};
+ $opts->{SSL_cipher_list} = $self->{ciphers} if defined $self->{ciphers};
+ $opts->{SSL_key_file} = $self->{key} if defined $self->{key};
+ $opts->{SSL_use_cert} = (defined $self->{cert}) ? 1 : 0;
+ $opts->{SSL_verify_mode} = (defined $self->{ca}) ? IO::Socket::SSL::SSL_VERIFY_PEER : IO::Socket::SSL::SSL_VERIFY_NONE;
+ $opts->{SSL_version} = (defined $self->{version}) ? $self->{version} : 'SSLv23:!SSLv2:!SSLv3';
+
+ return IO::Socket::SSL->new(%$opts);
+}
1;
diff --git a/lib/perl/lib/Thrift/SSLSocket.pm b/lib/perl/lib/Thrift/SSLSocket.pm
index 0d2edf8..046692e 100644
--- a/lib/perl/lib/Thrift/SSLSocket.pm
+++ b/lib/perl/lib/Thrift/SSLSocket.pm
@@ -29,10 +29,32 @@
package Thrift::SSLSocket;
-# TODO: Does not provide cipher selection or authentication hooks yet.
-
use base qw( Thrift::Socket );
+#
+# Construction and usage
+#
+# my $opts = {}
+# my $socket = new Thrift::SSLSocket(\%opts);
+#
+# options:
+#
+# Any option from Socket.pm is valid, and then:
+#
+# ca => certificate authority file (PEM file) to authenticate the
+# server against; if not specified then the server is not
+# authenticated
+# cert => certificate to use as the client; if not specified then
+# the client does not present one but still connects using
+# secure protocol
+# ciphers => allowed cipher list
+# (see http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS)
+# key => certificate key for "cert" option
+# version => acceptable SSL/TLS versions - if not specified then the
+# default is to use SSLv23 handshake but only negotiate
+# at TLSv1.0 or later
+#
+
sub new
{
my $classname = shift;
@@ -44,10 +66,20 @@
sub __open
{
my $self = shift;
- return IO::Socket::SSL->new(PeerAddr => $self->{host},
- PeerPort => $self->{port},
- Proto => 'tcp',
- Timeout => $self->{sendTimeout} / 1000);
+ my $opts = {PeerAddr => $self->{host},
+ PeerPort => $self->{port},
+ Proto => 'tcp',
+ Timeout => $self->{sendTimeout} / 1000};
+
+ $opts->{SSL_ca_file} = $self->{ca} if defined $self->{ca};
+ $opts->{SSL_cert_file} = $self->{cert} if defined $self->{cert};
+ $opts->{SSL_cipher_list} = $self->{ciphers} if defined $self->{ciphers};
+ $opts->{SSL_key_file} = $self->{key} if defined $self->{key};
+ $opts->{SSL_use_cert} = (defined $self->{cert}) ? 1 : 0;
+ $opts->{SSL_verify_mode} = (defined $self->{ca}) ? IO::Socket::SSL::SSL_VERIFY_PEER : IO::Socket::SSL::SSL_VERIFY_NONE;
+ $opts->{SSL_version} = (defined $self->{version}) ? $self->{version} : 'SSLv23:!SSLv2:!SSLv3';
+
+ return IO::Socket::SSL->new(%$opts);
}
sub __close
@@ -61,10 +93,10 @@
sub __recv
{
- my $self = shift;
- my $sock = shift;
- my $len = shift;
- my $buf = undef;
+ my $self = shift;
+ my $sock = shift;
+ my $len = shift;
+ my $buf = undef;
if ($sock) {
sysread($sock, $buf, $len);
}
diff --git a/lib/perl/lib/Thrift/Socket.pm b/lib/perl/lib/Thrift/Socket.pm
index eaf8b9e..c8e333b 100644
--- a/lib/perl/lib/Thrift/Socket.pm
+++ b/lib/perl/lib/Thrift/Socket.pm
@@ -31,23 +31,48 @@
use base qw( Thrift::Transport );
+#
+# Construction and usage
+#
+# my $opts = {}
+# my $socket = new Thrift::Socket(\%opts);
+#
+# options:
+#
+# host => host to connect to
+# port => port to connect to
+# sendTimeout => timeout used for send and for connect
+# recvTimeout => timeout used for recv
+#
+
sub new
{
- my $classname = shift;
- my $host = shift || "localhost";
- my $port = shift || 9090;
- my $debugHandler = shift;
+ my $classname = shift;
+ my $opts = shift;
+ # default settings:
my $self = {
- host => $host,
- port => $port,
- debugHandler => $debugHandler,
- debug => 0,
- sendTimeout => 10000,
+ host => 'localhost',
+ port => 9090,
recvTimeout => 10000,
- handle => undef,
+ sendTimeout => 10000,
+
+ handle => undef
};
+ if (defined $opts and ref $opts eq ref {}) {
+
+ # argument is a hash of options so override the defaults
+ $self->{$_} = $opts->{$_} for keys %$opts;
+
+ } else {
+
+ # older style constructor takes 3 arguments, none of which are required
+ $self->{host} = $opts || 'localhost';
+ $self->{port} = shift || 9090;
+
+ }
+
return bless($self,$classname);
}
@@ -70,19 +95,6 @@
#
-#Sets debugging output on or off
-#
-# @param bool $debug
-#
-sub setDebug
-{
- my $self = shift;
- my $debug = shift;
-
- $self->{debug} = $debug;
-}
-
-#
# Tests whether this is open
#
# @return bool true if the socket is open
@@ -107,11 +119,6 @@
my $sock = $self->__open() || do {
my $error = ref($self).': Could not connect to '.$self->{host}.':'.$self->{port}.' ('.$!.')';
-
- if ($self->{debug}) {
- $self->{debugHandler}->($error);
- }
-
die new Thrift::TException($error);
};
@@ -125,7 +132,7 @@
{
my $self = shift;
if( defined $self->{handle} ) {
- $self->__close();
+ $self->__close();
}
}
@@ -216,7 +223,7 @@
my $sent = $self->__send($sockets[0], $buf);
if (!defined $sent || $sent == 0 ) {
-
+
die new Thrift::TException(ref($self).': Could not write '.length($buf).' bytes '.
$self->{host}.':'.$self->{host});
@@ -259,7 +266,7 @@
#
sub __close
{
- my $self = shift;
+ my $self = shift;
CORE::close(($self->{handle}->handles())[0]);
}
@@ -272,12 +279,12 @@
#
sub __recv
{
- my $self = shift;
- my $sock = shift;
- my $len = shift;
- my $buf = undef;
- $sock->recv($buf, $len);
- return $buf;
+ my $self = shift;
+ my $sock = shift;
+ my $len = shift;
+ my $buf = undef;
+ $sock->recv($buf, $len);
+ return $buf;
}
#
diff --git a/test/erl/src/test_client.erl b/test/erl/src/test_client.erl
index 50ffc64..c916cee 100644
--- a/test/erl/src/test_client.erl
+++ b/test/erl/src/test_client.erl
@@ -51,8 +51,9 @@
ssl:start(),
SslOptions =
{ssloptions, [
- {certfile, "../keys/client.crt"}
- ,{keyfile, "../keys/server.key"}
+ {cacertfile, "../keys/CA.pem"},
+ {certfile, "../keys/client.pem"},
+ {keyfile, "../keys/client.key"}
]},
Opts#options{client_opts = [{ssltransport, true} | [SslOptions | Opts#options.client_opts]]};
"--protocol=" ++ Proto ->
diff --git a/test/erl/src/test_thrift_server.erl b/test/erl/src/test_thrift_server.erl
index f3ed2f7..dad8dec 100644
--- a/test/erl/src/test_thrift_server.erl
+++ b/test/erl/src/test_thrift_server.erl
@@ -51,7 +51,7 @@
ssl:start(),
SslOptions =
{ssloptions, [
- {certfile, "../keys/server.crt"}
+ {certfile, "../keys/server.pem"}
,{keyfile, "../keys/server.key"}
]},
Opts#options{server_opts = [{ssltransport, true} | [SslOptions | Opts#options.server_opts]]};
diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json
index 4ca2050..d60997d 100644
--- a/test/known_failures_Linux.json
+++ b/test/known_failures_Linux.json
@@ -34,8 +34,6 @@
"cpp-java_compact_http-ip-ssl",
"cpp-java_json_http-ip",
"cpp-java_json_http-ip-ssl",
- "cpp-perl_binary_buffered-ip-ssl",
- "cpp-perl_binary_framed-ip-ssl",
"csharp-c_glib_binary_buffered-ip-ssl",
"csharp-c_glib_binary_framed-ip-ssl",
"csharp-c_glib_compact_buffered-ip-ssl",
@@ -140,8 +138,6 @@
"d-nodejs_json_buffered-ip-ssl",
"d-nodejs_json_framed-ip",
"d-nodejs_json_framed-ip-ssl",
- "d-perl_binary_buffered-ip-ssl",
- "d-perl_binary_framed-ip-ssl",
"d-py3_binary-accel_buffered-ip",
"d-py3_binary-accel_buffered-ip-ssl",
"d-py3_binary-accel_framed-ip",
@@ -184,8 +180,6 @@
"d-py_json_framed-ip-ssl",
"erl-nodejs_binary_buffered-ip",
"erl-nodejs_compact_buffered-ip",
- "erl-perl_binary_buffered-ip-ssl",
- "erl-perl_binary_framed-ip-ssl",
"erl-rb_binary-accel_buffered-ip",
"erl-rb_binary-accel_framed-ip",
"erl-rb_binary_buffered-ip",
@@ -216,8 +210,6 @@
"go-java_json_http-ip",
"go-java_json_http-ip-ssl",
"go-nodejs_json_framed-ip",
- "go-perl_binary_buffered-ip-ssl",
- "go-perl_binary_framed-ip-ssl",
"hs-csharp_binary_framed-ip",
"hs-csharp_compact_framed-ip",
"hs-csharp_json_framed-ip",
@@ -229,21 +221,8 @@
"java-d_compact_buffered-ip",
"java-d_compact_buffered-ip-ssl",
"java-d_compact_framed-ip",
- "java-perl_binary_buffered-ip-ssl",
- "java-perl_binary_fastframed-framed-ip-ssl",
- "java-perl_binary_framed-ip-ssl",
- "nodejs-perl_binary_buffered-ip-ssl",
- "nodejs-perl_binary_framed-ip-ssl",
+ "perl-erl_binary_buffered-ip-ssl",
+ "perl-erl_binary_framed-ip-ssl",
"perl-perl_binary_buffered-ip-ssl",
- "perl-perl_binary_framed-ip-ssl",
- "perl-php_binary_framed-ip",
- "py-cpp_compact_buffered-ip",
- "py-perl_accel-binary_buffered-ip-ssl",
- "py-perl_accel-binary_framed-ip-ssl",
- "py-perl_binary_buffered-ip-ssl",
- "py-perl_binary_framed-ip-ssl",
- "py3-perl_accel-binary_buffered-ip-ssl",
- "py3-perl_accel-binary_framed-ip-ssl",
- "py3-perl_binary_buffered-ip-ssl",
- "py3-perl_binary_framed-ip-ssl"
+ "perl-perl_binary_framed-ip-ssl"
]
diff --git a/test/perl/TestClient.pl b/test/perl/TestClient.pl
index 7ec32bf..483c964 100755
--- a/test/perl/TestClient.pl
+++ b/test/perl/TestClient.pl
@@ -47,15 +47,19 @@
Usage: $0 [OPTIONS]
Options: (default)
+ --ca CA to validate server with.
--cert Certificate to use.
Required if using --ssl.
+ --ciphers Acceptable cipher list.
--domain-socket <file> Use a unix domain socket.
--help Show usage.
+ --key Certificate key.
+ Required if using --ssl.
--port <portnum> 9090 Port to use.
--protocol {binary} binary Protocol to use.
--ssl If present, use SSL.
--transport {buffered|framed} buffered Transport to use.
-
+
EOF
}
@@ -66,7 +70,10 @@
);
GetOptions(\%opts, qw (
+ ca=s
cert=s
+ ciphers=s
+ key=s
domain-socket=s
help
host=s
@@ -81,18 +88,13 @@
exit 0;
}
-if ($opts{ssl} and not defined $opts{cert}) {
- usage();
- exit 1;
-}
-
my $socket = undef;
if ($opts{"domain-socket"}) {
$socket = new Thrift::UnixSocket($opts{"domain-socket"});
} elsif ($opts{ssl}) {
- $socket = new Thrift::SSLSocket($opts{host}, $opts{port});
+ $socket = new Thrift::SSLSocket(\%opts);
} else {
- $socket = new Thrift::Socket($opts{host}, $opts{port});
+ $socket = new Thrift::Socket($opts{host}, $opts{port});
}
my $transport;
@@ -117,7 +119,7 @@
eval {
$transport->open();
-};
+};
if($@){
die(Dumper($@));
}
diff --git a/test/perl/TestServer.pl b/test/perl/TestServer.pl
index 4e0cc79..e2835f4 100644
--- a/test/perl/TestServer.pl
+++ b/test/perl/TestServer.pl
@@ -50,7 +50,8 @@
Options: (default)
--ca Certificate authority file (optional).
--cert Certificate file.
- Required if using --ssl.
+ Required if using --ssl.
+ --ciphers Acceptable cipher list.
--domain-socket <file> Use a unix domain socket.
--help Show usage.
--key Private key file for certificate.
@@ -60,7 +61,7 @@
--protocol {binary} binary Protocol to use.
--ssl If present, use SSL/TLS.
--transport {buffered|framed} buffered Transport to use.
-
+
EOF
}
@@ -73,6 +74,7 @@
GetOptions(\%opts, qw (
ca=s
cert=s
+ ciphers=s
domain-socket=s
help
host=s
@@ -133,7 +135,7 @@
print "Starting \"simple\" server ($opts{transport}/$opts{protocol}) listen on: $listening_on\n";
$server->serve();
-###
+###
### Test server implementation
###
@@ -148,7 +150,7 @@
}
sub testVoid() {
- print("testVoid()\n");
+ print("testVoid()\n");
}
sub testString() {
@@ -320,7 +322,7 @@
sub testMapMap() {
my $self = shift;
my $hello = shift;
-
+
printf("testMapMap(%d)\n", $hello);
my $result = { 4 => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }, -4 => { -1 => -1, -2 => -2, -3 => -3, -4 => -4 } };
return $result;
@@ -352,7 +354,7 @@
my $arg3 = shift;
my $arg4 = shift;
my $arg5 = shift;
-
+
print("testMulti()\n");
return new ThriftTest::Xtruct({string_thing => "Hello2", byte_thing => $arg0, i32_thing => $arg1, i64_thing => $arg2});
}
diff --git a/test/tests.json b/test/tests.json
index 5de36f5..e228928 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -414,7 +414,9 @@
"-Igen-perl/",
"-I../../lib/perl/lib/",
"TestClient.pl",
- "--cert=../keys/client.pem"
+ "--ca=../keys/CA.pem",
+ "--cert=../keys/client.pem",
+ "--key=../keys/client.key"
]
},
"server": {
@@ -423,6 +425,7 @@
"-Igen-perl/",
"-I../../lib/perl/lib/",
"TestServer.pl",
+ "--ca=../keys/CA.pem",
"--cert=../keys/server.pem",
"--key=../keys/server.key"
]