Fix a memory leak in the mem bio
[oweals/openssl.git] / test / recipes / 70-test_sslrecords.t
index c8302042343db2ad92795ec64b64ace98c82091d..0e38308fbd1d8c2fcd17ad5286b08caf22e95ce5 100644 (file)
@@ -1,12 +1,14 @@
 #! /usr/bin/env perl
-# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
 #
-# Licensed under the OpenSSL license (the "License").  You may not use
+# Licensed under the Apache License 2.0 (the "License").  You may not use
 # this file except in compliance with the License.  You can obtain a copy
 # in the file LICENSE in the source distribution or at
 # https://www.openssl.org/source/license.html
 
 use strict;
+use feature 'state';
+
 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
 use OpenSSL::Test::Utils;
 use TLSProxy::Proxy;
@@ -35,6 +37,7 @@ my $proxy = TLSProxy::Proxy->new(
 );
 
 my $boundary_test_type;
+my $fatal_alert = 0;    # set by filters at expected fatal alerts
 
 #Test 1: Injecting out of context empty records should fail
 my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
@@ -42,7 +45,7 @@ my $inject_recs_num = 1;
 $proxy->serverflags("-tls1_2");
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
 plan tests => 18;
-ok(TLSProxy::Message->fail(), "Out of context empty records test");
+ok($fatal_alert, "Out of context empty records test");
 
 #Test 2: Injecting in context empty records should succeed
 $proxy->clear();
@@ -52,21 +55,23 @@ $proxy->start();
 ok(TLSProxy::Message->success(), "In context empty records test");
 
 #Test 3: Injecting too many in context empty records should fail
+$fatal_alert = 0;
 $proxy->clear();
 #We allow 32 consecutive in context empty records
 $inject_recs_num = 33;
 $proxy->serverflags("-tls1_2");
 $proxy->start();
-ok(TLSProxy::Message->fail(), "Too many in context empty records test");
+ok($fatal_alert, "Too many in context empty records test");
 
 #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to
 #        send back an alert of its own because it cannot handle fragmented
 #        alerts
+$fatal_alert = 0;
 $proxy->clear();
 $proxy->filter(\&add_frag_alert_filter);
 $proxy->serverflags("-tls1_2");
 $proxy->start();
-ok(TLSProxy::Message->fail(), "Fragmented alert records test");
+ok($fatal_alert, "Fragmented alert records test");
 
 #Run some SSLv2 ClientHello tests
 
@@ -122,51 +127,56 @@ ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
 #Unrecognised record type tests
 
 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
+$fatal_alert = 0;
 $proxy->clear();
 $proxy->serverflags("-tls1_2");
 $proxy->filter(\&add_unknown_record_type);
 $proxy->start();
-ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
+ok($fatal_alert, "Unrecognised record type in TLS1.2");
 
 SKIP: {
     skip "TLSv1.1 disabled", 1 if disabled("tls1_1");
 
     #Test 11: Sending an unrecognised record type in TLS1.1 should fail
+    $fatal_alert = 0;
     $proxy->clear();
     $proxy->clientflags("-tls1_1");
     $proxy->start();
-    ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
+    ok($fatal_alert, "Unrecognised record type in TLS1.1");
 }
 
 #Test 12: Sending a different record version in TLS1.2 should fail
+$fatal_alert = 0;
 $proxy->clear();
 $proxy->clientflags("-tls1_2");
 $proxy->filter(\&change_version);
 $proxy->start();
-ok(TLSProxy::Message->fail(), "Changed record version in TLS1.2");
+ok($fatal_alert, "Changed record version in TLS1.2");
 
 #TLS1.3 specific tests
 SKIP: {
     skip "TLSv1.3 disabled", 6 if disabled("tls1_3");
 
-    #Test 13: Sending a different record version in TLS1.3 should succeed
+    #Test 13: Sending a different record version in TLS1.3 should fail
     $proxy->clear();
     $proxy->filter(\&change_version);
     $proxy->start();
-    ok(TLSProxy::Message->success(), "Changed record version in TLS1.3");
+    ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3");
 
     #Test 14: Sending an unrecognised record type in TLS1.3 should fail
+    $fatal_alert = 0;
     $proxy->clear();
     $proxy->filter(\&add_unknown_record_type);
     $proxy->start();
-    ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.3");
+    ok($fatal_alert, "Unrecognised record type in TLS1.3");
 
     #Test 15: Sending an outer record type other than app data once encrypted
     #should fail
+    $fatal_alert = 0;
     $proxy->clear();
     $proxy->filter(\&change_outer_record_type);
     $proxy->start();
-    ok(TLSProxy::Message->fail(), "Wrong outer record type in TLS1.3");
+    ok($fatal_alert, "Wrong outer record type in TLS1.3");
 
     use constant {
         DATA_AFTER_SERVER_HELLO => 0,
@@ -176,36 +186,41 @@ SKIP: {
 
     #Test 16: Sending a ServerHello which doesn't end on a record boundary
     #         should fail
+    $fatal_alert = 0;
     $proxy->clear();
     $boundary_test_type = DATA_AFTER_SERVER_HELLO;
     $proxy->filter(\&not_on_record_boundary);
     $proxy->start();
-    ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (ServerHello)");
+    ok($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)");
 
     #Test 17: Sending a Finished which doesn't end on a record boundary
     #         should fail
+    $fatal_alert = 0;
     $proxy->clear();
     $boundary_test_type = DATA_AFTER_FINISHED;
     $proxy->filter(\&not_on_record_boundary);
     $proxy->start();
-    ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (Finished)");
+    ok($fatal_alert, "Record not on boundary in TLS1.3 (Finished)");
 
     #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
     #         should fail
+    $fatal_alert = 0;
     $proxy->clear();
     $boundary_test_type = DATA_AFTER_KEY_UPDATE;
     $proxy->filter(\&not_on_record_boundary);
     $proxy->start();
-    ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (KeyUpdate)");
+    ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)");
  }
 
 
 sub add_empty_recs_filter
 {
     my $proxy = shift;
+    my $records = $proxy->record_list;
 
     # We're only interested in the initial ClientHello
     if ($proxy->flight != 0) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
         return;
     }
 
@@ -221,18 +236,19 @@ sub add_empty_recs_filter
             "",
             ""
         );
-
-        push @{$proxy->record_list}, $record;
+        push @{$records}, $record;
     }
 }
 
 sub add_frag_alert_filter
 {
     my $proxy = shift;
+    my $records = $proxy->record_list;
     my $byte;
 
     # We're only interested in the initial ClientHello
     if ($proxy->flight != 0) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
         return;
     }
 
@@ -262,7 +278,7 @@ sub add_frag_alert_filter
         $byte,
         $byte
     );
-    push @{$proxy->record_list}, $record;
+    push @{$records}, $record;
 
     # And finally the description (Unexpected message) in a third record
     $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
@@ -277,7 +293,7 @@ sub add_frag_alert_filter
         $byte,
         $byte
     );
-    push @{$proxy->record_list}, $record;
+    push @{$records}, $record;
 }
 
 sub add_sslv2_filter
@@ -430,17 +446,22 @@ sub add_sslv2_filter
 sub add_unknown_record_type
 {
     my $proxy = shift;
+    my $records = $proxy->record_list;
+    state $added_record;
 
     # We'll change a record after the initial version neg has taken place
-    if ($proxy->flight != 1) {
+    if ($proxy->flight == 0) {
+        $added_record = 0;
+        return;
+    } elsif ($proxy->flight != 1 || $added_record) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
         return;
     }
 
-    my $lastrec = ${$proxy->record_list}[-1];
     my $record = TLSProxy::Record->new(
         1,
         TLSProxy::Record::RT_UNKNOWN,
-        $lastrec->version(),
+        @{$records}[-1]->version(),
         1,
         0,
         1,
@@ -457,64 +478,86 @@ sub add_unknown_record_type
     $i++;
 
     splice @{$proxy->record_list}, $i, 0, $record;
+    $added_record = 1;
 }
 
 sub change_version
 {
     my $proxy = shift;
+    my $records = $proxy->record_list;
 
     # We'll change a version after the initial version neg has taken place
-    if ($proxy->flight != 2) {
+    if ($proxy->flight != 1) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 70;
         return;
     }
 
-    (${$proxy->record_list}[-1])->version(TLSProxy::Record::VERS_TLS_1_1);
+    if ($#{$records} > 1) {
+        # ... typically in ServerHelloDone
+        @{$records}[-1]->version(TLSProxy::Record::VERS_TLS_1_1);
+    }
 }
 
 sub change_outer_record_type
 {
     my $proxy = shift;
+    my $records = $proxy->record_list;
 
     # We'll change a record after the initial version neg has taken place
     if ($proxy->flight != 1) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
         return;
     }
 
-    #Find ServerHello record and change record after that
-    my $i;
-    for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
-        next;
+    # Find CCS record and change record after that
+    my $i = 0;
+    foreach my $record (@{$records}) {
+        last if $record->content_type == TLSProxy::Record::RT_CCS;
+        $i++;
+    }
+    if (defined(${$records}[++$i])) {
+        ${$records}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
     }
-    #Skip CCS and ServerHello
-    $i += 2;
-    ${$proxy->record_list}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
 }
 
 sub not_on_record_boundary
 {
     my $proxy = shift;
+    my $records = $proxy->record_list;
     my $data;
 
     #Find server's first flight
     if ($proxy->flight != 1) {
+        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
         return;
     }
 
     if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
         #Merge the ServerHello and EncryptedExtensions records into one
-        my $i;
-        for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
-            next;
+        my $i = 0;
+        foreach my $record (@{$records}) {
+            if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
+                $record->{sent} = 1;    # pretend it's sent already
+                last;
+            }
+            $i++;
         }
-        $data = ${$proxy->record_list}[$i]->data();
-        $data .= ${$proxy->record_list}[$i + 1]->decrypt_data();
-        ${$proxy->record_list}[$i]->data($data);
-        ${$proxy->record_list}[$i]->len(length $data);
 
-        #Delete the old EncryptedExtensions record
-        splice @{$proxy->record_list}, $i + 1, 1;
+        if (defined(${$records}[$i+1])) {
+            $data = ${$records}[$i]->data();
+            $data .= ${$records}[$i+1]->decrypt_data();
+            ${$records}[$i+1]->data($data);
+            ${$records}[$i+1]->len(length $data);
+
+            #Delete the old ServerHello record
+            splice @{$records}, $i, 1;
+        }
     } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
-        $data = ${$proxy->record_list}[-1]->decrypt_data;
+        return if @{$proxy->{message_list}}[-1]->{mt}
+                  != TLSProxy::Message::MT_FINISHED;
+
+        my $last_record = @{$records}[-1];
+        $data = $last_record->decrypt_data;
 
         #Add a KeyUpdate message onto the end of the Finished record
         my $keyupdate = pack "C5",
@@ -528,15 +571,18 @@ sub not_on_record_boundary
         $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
 
         #Update the record
-        ${$proxy->record_list}[-1]->data($data);
-        ${$proxy->record_list}[-1]->len(length $data);
+        $last_record->data($data);
+        $last_record->len(length $data);
     } else {
+        return if @{$proxy->{message_list}}[-1]->{mt}
+                  != TLSProxy::Message::MT_FINISHED;
+
         #KeyUpdates must end on a record boundary
 
         my $record = TLSProxy::Record->new(
             1,
             TLSProxy::Record::RT_APPLICATION_DATA,
-            TLSProxy::Record::VERS_TLS_1_0,
+            TLSProxy::Record::VERS_TLS_1_2,
             0,
             0,
             0,
@@ -558,6 +604,6 @@ sub not_on_record_boundary
 
         $record->data($data);
         $record->len(length $data);
-        push @{$proxy->record_list}, $record;
+        push @{$records}, $record;
     }
 }