aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2016-08-02 17:24:54 +0100
committerMatt Caswell <matt@openssl.org>2016-08-15 23:14:30 +0100
commita2a0c86bb0d602253d02ded2a848ed69e8cc425a (patch)
tree55052ad87615adcd3848bd0ff2abfc9c87d227dd
parenta01c86a25198921c5b8adb45c9379088ace4e42e (diff)
downloadopenssl-a2a0c86bb0d602253d02ded2a848ed69e8cc425a.tar.gz
Add some SSLv2 ClientHello tests
Test that we handle a TLS ClientHello in an SSLv2 record correctly. Reviewed-by: Tim Hudson <tjh@openssl.org>
-rw-r--r--test/recipes/70-test_sslrecords.t198
-rw-r--r--util/TLSProxy/Message.pm3
-rw-r--r--util/TLSProxy/Record.pm14
-rw-r--r--util/TLSProxy/ServerHello.pm20
4 files changed, 228 insertions, 7 deletions
diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t
index 0ae018a07d..8af236fa32 100644
--- a/test/recipes/70-test_sslrecords.t
+++ b/test/recipes/70-test_sslrecords.t
@@ -38,7 +38,7 @@ my $proxy = TLSProxy::Proxy->new(
my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
my $inject_recs_num = 1;
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
-plan tests => 4;
+plan tests => 9;
ok(TLSProxy::Message->fail(), "Out of context empty records test");
#Test 2: Injecting in context empty records should succeed
@@ -62,6 +62,52 @@ $proxy->filter(\&add_frag_alert_filter);
$proxy->start();
ok(!TLSProxy::Message->end(), "Fragmented alert records test");
+#Run some SSLv2 ClientHello tests
+
+use constant {
+ TLSV1_2_IN_SSLV2 => 0,
+ SSLV2_IN_SSLV2 => 1,
+ FRAGMENTED_IN_TLSV1_2 => 2,
+ FRAGMENTED_IN_SSLV2 => 3,
+ ALERT_BEFORE_SSLV2 => 4
+};
+#Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
+#my $sslv2testtype = TLSV1_2_IN_SSLV2;
+my $sslv2testtype = TLSV1_2_IN_SSLV2;
+$proxy->clear();
+$proxy->filter(\&add_sslv2_filter);
+$proxy->start();
+ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
+
+#Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
+# support this so it should fail. We actually treat it as an unknown
+# protocol so we don't even send an alert in this case.
+$sslv2testtype = SSLV2_IN_SSLV2;
+$proxy->clear();
+$proxy->start();
+ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
+
+#Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
+# at all, but it gives us confidence that Test 8 fails for the right
+# reasons
+$sslv2testtype = FRAGMENTED_IN_TLSV1_2;
+$proxy->clear();
+$proxy->start();
+ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
+
+#Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
+# record; and another TLS1.2 record. This isn't allowed so should fail
+$sslv2testtype = FRAGMENTED_IN_SSLV2;
+$proxy->clear();
+$proxy->start();
+ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
+
+#Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
+# fail because an SSLv2 ClientHello must be the first record.
+$sslv2testtype = ALERT_BEFORE_SSLV2;
+$proxy->clear();
+$proxy->start();
+ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
sub add_empty_recs_filter
{
my $proxy = shift;
@@ -79,6 +125,7 @@ sub add_empty_recs_filter
0,
0,
0,
+ 0,
"",
""
);
@@ -117,6 +164,7 @@ sub add_frag_alert_filter
TLSProxy::Record::RT_ALERT,
TLSProxy::Record::VERS_TLS_1_2,
1,
+ 0,
1,
1,
$byte,
@@ -131,6 +179,7 @@ sub add_frag_alert_filter
TLSProxy::Record::RT_ALERT,
TLSProxy::Record::VERS_TLS_1_2,
1,
+ 0,
1,
1,
$byte,
@@ -138,3 +187,150 @@ sub add_frag_alert_filter
);
push @{$proxy->record_list}, $record;
}
+
+sub add_sslv2_filter
+{
+ my $proxy = shift;
+ my $clienthello;
+ my $record;
+
+ # We're only interested in the initial ClientHello
+ if ($proxy->flight != 0) {
+ return;
+ }
+
+ # Ditch the real ClientHello - we're going to replace it with our own
+ shift @{$proxy->record_list};
+
+ if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
+ my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
+ TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
+ my $alertlen = length $alert;
+ $record = TLSProxy::Record->new(
+ 0,
+ TLSProxy::Record::RT_ALERT,
+ TLSProxy::Record::VERS_TLS_1_2,
+ $alertlen,
+ 0,
+ $alertlen,
+ $alertlen,
+ $alert,
+ $alert
+ );
+
+ push @{$proxy->record_list}, $record;
+ }
+
+ if ($sslv2testtype == ALERT_BEFORE_SSLV2
+ || $sslv2testtype == TLSV1_2_IN_SSLV2
+ || $sslv2testtype == SSLV2_IN_SSLV2) {
+ # This is an SSLv2 format ClientHello
+ $clienthello =
+ pack "C44",
+ 0x01, # ClientHello
+ 0x03, 0x03, #TLSv1.2
+ 0x00, 0x03, # Ciphersuites len
+ 0x00, 0x00, # Session id len
+ 0x00, 0x20, # Challenge len
+ 0x00, 0x00, 0x2f, #AES128-SHA
+ 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
+ 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
+ 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
+
+ if ($sslv2testtype == SSLV2_IN_SSLV2) {
+ # Set the version to "real" SSLv2
+ vec($clienthello, 1, 8) = 0x00;
+ vec($clienthello, 2, 8) = 0x02;
+ }
+
+ my $chlen = length $clienthello;
+
+ $record = TLSProxy::Record->new(
+ 0,
+ TLSProxy::Record::RT_HANDSHAKE,
+ TLSProxy::Record::VERS_TLS_1_2,
+ $chlen,
+ 1, #SSLv2
+ $chlen,
+ $chlen,
+ $clienthello,
+ $clienthello
+ );
+
+ push @{$proxy->record_list}, $record;
+ } else {
+ # For this test we're using a real TLS ClientHello
+ $clienthello =
+ pack "C49",
+ 0x01, # ClientHello
+ 0x00, 0x00, 0x2D, # Message length
+ 0x03, 0x03, # TLSv1.2
+ 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
+ 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
+ 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
+ 0x00, # Session id len
+ 0x00, 0x04, # Ciphersuites len
+ 0x00, 0x2f, # AES128-SHA
+ 0x00, 0xff, # Empty reneg info SCSV
+ 0x01, # Compression methods len
+ 0x00, # Null compression
+ 0x00, 0x00; # Extensions len
+
+ # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
+ # We deliberately split the second record prior to the Challenge/Random
+ # and set the first byte of the random to 1. This makes the second SSLv2
+ # record look like an SSLv2 ClientHello
+ my $frag1 = substr $clienthello, 0, 6;
+ my $frag2 = substr $clienthello, 6, 32;
+ my $frag3 = substr $clienthello, 38;
+
+ my $fraglen = length $frag1;
+ $record = TLSProxy::Record->new(
+ 0,
+ TLSProxy::Record::RT_HANDSHAKE,
+ TLSProxy::Record::VERS_TLS_1_2,
+ $fraglen,
+ 0,
+ $fraglen,
+ $fraglen,
+ $frag1,
+ $frag1
+ );
+ push @{$proxy->record_list}, $record;
+
+ $fraglen = length $frag2;
+ my $recvers;
+ if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
+ $recvers = 1;
+ } else {
+ $recvers = 0;
+ }
+ $record = TLSProxy::Record->new(
+ 0,
+ TLSProxy::Record::RT_HANDSHAKE,
+ TLSProxy::Record::VERS_TLS_1_2,
+ $fraglen,
+ $recvers,
+ $fraglen,
+ $fraglen,
+ $frag2,
+ $frag2
+ );
+ push @{$proxy->record_list}, $record;
+
+ $fraglen = length $frag3;
+ $record = TLSProxy::Record->new(
+ 0,
+ TLSProxy::Record::RT_HANDSHAKE,
+ TLSProxy::Record::VERS_TLS_1_2,
+ $fraglen,
+ 0,
+ $fraglen,
+ $fraglen,
+ $frag3,
+ $frag3
+ );
+ push @{$proxy->record_list}, $record;
+ }
+
+}
diff --git a/util/TLSProxy/Message.pm b/util/TLSProxy/Message.pm
index b8db22fb85..321e080ea3 100644
--- a/util/TLSProxy/Message.pm
+++ b/util/TLSProxy/Message.pm
@@ -37,7 +37,8 @@ use constant {
#Alert descriptions
use constant {
AL_DESC_CLOSE_NOTIFY => 0,
- AL_DESC_UNEXPECTED_MESSAGE => 10
+ AL_DESC_UNEXPECTED_MESSAGE => 10,
+ AL_DESC_NO_RENEGOTIATION => 100
};
my %message_type = (
diff --git a/util/TLSProxy/Record.pm b/util/TLSProxy/Record.pm
index 2a605e33a7..423bad3bf1 100644
--- a/util/TLSProxy/Record.pm
+++ b/util/TLSProxy/Record.pm
@@ -98,6 +98,7 @@ sub get_records
$content_type,
$version,
$len,
+ 0,
$len_real,
$decrypt_len,
substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real),
@@ -167,6 +168,7 @@ sub new
$content_type,
$version,
$len,
+ $sslv2,
$len_real,
$decrypt_len,
$data,
@@ -177,6 +179,7 @@ sub new
content_type => $content_type,
version => $version,
len => $len,
+ sslv2 => $sslv2,
len_real => $len_real,
decrypt_len => $decrypt_len,
data => $data,
@@ -247,7 +250,11 @@ sub reconstruct_record
my $self = shift;
my $data;
- $data = pack('Cnn', $self->content_type, $self->version, $self->len);
+ if ($self->sslv2) {
+ $data = pack('n', $self->len | 0x8000);
+ } else {
+ $data = pack('Cnn', $self->content_type, $self->version, $self->len);
+ }
$data .= $self->data;
return $data;
@@ -269,6 +276,11 @@ sub version
my $self = shift;
return $self->{version};
}
+sub sslv2
+{
+ my $self = shift;
+ return $self->{sslv2};
+}
sub len_real
{
my $self = shift;
diff --git a/util/TLSProxy/ServerHello.pm b/util/TLSProxy/ServerHello.pm
index ee2fd727d2..79a8be9a89 100644
--- a/util/TLSProxy/ServerHello.pm
+++ b/util/TLSProxy/ServerHello.pm
@@ -56,13 +56,25 @@ sub parse
my $comp_meth = unpack('C', substr($self->data, $ptr));
$ptr++;
my $extensions_len = unpack('n', substr($self->data, $ptr));
- $ptr += 2;
+ if (!defined $extensions_len) {
+ $extensions_len = 0;
+ } else {
+ $ptr += 2;
+ }
#For now we just deal with this as a block of data. In the future we will
#want to parse this
- my $extension_data = substr($self->data, $ptr);
+ my $extension_data;
+ if ($extensions_len != 0) {
+ $extension_data = substr($self->data, $ptr);
- if (length($extension_data) != $extensions_len) {
- die "Invalid extension length\n";
+ if (length($extension_data) != $extensions_len) {
+ die "Invalid extension length\n";
+ }
+ } else {
+ if (length($self->data) != $ptr) {
+ die "Invalid extension length\n";
+ }
+ $extension_data = "";
}
my %extensions = ();
while (length($extension_data) >= 4) {