2 # Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
4 # Licensed under the OpenSSL license (the "License"). You may not use
5 # this file except in compliance with the License. You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
10 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
11 use OpenSSL::Test::Utils;
14 my $test_name = "test_sslrecords";
17 plan skip_all => "TLSProxy isn't usable on $^O"
18 if $^O =~ /^(VMS|MSWin32)$/;
20 plan skip_all => "$test_name needs the dynamic engine feature enabled"
21 if disabled("engine") || disabled("dynamic-engine");
23 plan skip_all => "$test_name needs the sock feature enabled"
26 plan skip_all => "$test_name needs TLSv1.2 enabled"
27 if disabled("tls1_2");
29 $ENV{OPENSSL_ia32cap} = '~0x200000200000000';
30 my $proxy = TLSProxy::Proxy->new(
31 \&add_empty_recs_filter,
32 cmdstr(app(["openssl"]), display => 1),
33 srctop_file("apps", "server.pem"),
34 (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
37 #Test 1: Injecting out of context empty records should fail
38 my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
39 my $inject_recs_num = 1;
40 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
42 if (!disabled("tls1_1")) {
45 plan tests => $num_tests;
46 ok(TLSProxy::Message->fail(), "Out of context empty records test");
48 #Test 2: Injecting in context empty records should succeed
50 $content_type = TLSProxy::Record::RT_HANDSHAKE;
52 ok(TLSProxy::Message->success(), "In context empty records test");
54 #Test 3: Injecting too many in context empty records should fail
56 #We allow 32 consecutive in context empty records
57 $inject_recs_num = 33;
59 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
61 #Test 4: Injecting a fragmented fatal alert should fail. We actually expect no
62 # alerts to be sent from either side because *we* injected the fatal
63 # alert, i.e. this will look like a disorderly close
65 $proxy->filter(\&add_frag_alert_filter);
67 ok(!TLSProxy::Message->end(), "Fragmented alert records test");
69 #Run some SSLv2 ClientHello tests
72 TLSV1_2_IN_SSLV2 => 0,
74 FRAGMENTED_IN_TLSV1_2 => 2,
75 FRAGMENTED_IN_SSLV2 => 3,
76 ALERT_BEFORE_SSLV2 => 4
78 #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
79 my $sslv2testtype = TLSV1_2_IN_SSLV2;
81 $proxy->filter(\&add_sslv2_filter);
83 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
85 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
86 # support this so it should fail. We actually treat it as an unknown
87 # protocol so we don't even send an alert in this case.
88 $sslv2testtype = SSLV2_IN_SSLV2;
91 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
93 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
94 # at all, but it gives us confidence that Test 8 fails for the right
96 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
99 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
101 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
102 # record; and another TLS1.2 record. This isn't allowed so should fail
103 $sslv2testtype = FRAGMENTED_IN_SSLV2;
106 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
108 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
109 # fail because an SSLv2 ClientHello must be the first record.
110 $sslv2testtype = ALERT_BEFORE_SSLV2;
113 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
115 #Unregcognised record type tests
117 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
119 $proxy->filter(\&add_unknown_record_type);
121 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
123 #Test 11: Sending an unrecognised record type in TLS1.1 should fail
124 if (!disabled("tls1_1")) {
126 $proxy->clientflags("-tls1_1");
128 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
131 sub add_empty_recs_filter
135 # We're only interested in the initial ClientHello
136 if ($proxy->flight != 0) {
140 for (my $i = 0; $i < $inject_recs_num; $i++) {
141 my $record = TLSProxy::Record->new(
144 TLSProxy::Record::VERS_TLS_1_2,
153 push @{$proxy->record_list}, $record;
157 sub add_frag_alert_filter
162 # We're only interested in the initial ClientHello
163 if ($proxy->flight != 0) {
167 # Add a zero length fragment first
168 #my $record = TLSProxy::Record->new(
170 # TLSProxy::Record::RT_ALERT,
171 # TLSProxy::Record::VERS_TLS_1_2,
178 #push @{$proxy->record_list}, $record;
180 # Now add the alert level (Fatal) as a separate record
181 $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
182 my $record = TLSProxy::Record->new(
184 TLSProxy::Record::RT_ALERT,
185 TLSProxy::Record::VERS_TLS_1_2,
193 push @{$proxy->record_list}, $record;
195 # And finally the description (Unexpected message) in a third record
196 $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
197 $record = TLSProxy::Record->new(
199 TLSProxy::Record::RT_ALERT,
200 TLSProxy::Record::VERS_TLS_1_2,
208 push @{$proxy->record_list}, $record;
217 # We're only interested in the initial ClientHello
218 if ($proxy->flight != 0) {
222 # Ditch the real ClientHello - we're going to replace it with our own
223 shift @{$proxy->record_list};
225 if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
226 my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
227 TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
228 my $alertlen = length $alert;
229 $record = TLSProxy::Record->new(
231 TLSProxy::Record::RT_ALERT,
232 TLSProxy::Record::VERS_TLS_1_2,
241 push @{$proxy->record_list}, $record;
244 if ($sslv2testtype == ALERT_BEFORE_SSLV2
245 || $sslv2testtype == TLSV1_2_IN_SSLV2
246 || $sslv2testtype == SSLV2_IN_SSLV2) {
247 # This is an SSLv2 format ClientHello
252 0x00, 0x03, # Ciphersuites len
253 0x00, 0x00, # Session id len
254 0x00, 0x20, # Challenge len
255 0x00, 0x00, 0x2f, #AES128-SHA
256 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
257 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
258 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
260 if ($sslv2testtype == SSLV2_IN_SSLV2) {
261 # Set the version to "real" SSLv2
262 vec($clienthello, 1, 8) = 0x00;
263 vec($clienthello, 2, 8) = 0x02;
266 my $chlen = length $clienthello;
268 $record = TLSProxy::Record->new(
270 TLSProxy::Record::RT_HANDSHAKE,
271 TLSProxy::Record::VERS_TLS_1_2,
280 push @{$proxy->record_list}, $record;
282 # For this test we're using a real TLS ClientHello
286 0x00, 0x00, 0x2D, # Message length
287 0x03, 0x03, # TLSv1.2
288 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
289 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
290 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
291 0x00, # Session id len
292 0x00, 0x04, # Ciphersuites len
293 0x00, 0x2f, # AES128-SHA
294 0x00, 0xff, # Empty reneg info SCSV
295 0x01, # Compression methods len
296 0x00, # Null compression
297 0x00, 0x00; # Extensions len
299 # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
300 # We deliberately split the second record prior to the Challenge/Random
301 # and set the first byte of the random to 1. This makes the second SSLv2
302 # record look like an SSLv2 ClientHello
303 my $frag1 = substr $clienthello, 0, 6;
304 my $frag2 = substr $clienthello, 6, 32;
305 my $frag3 = substr $clienthello, 38;
307 my $fraglen = length $frag1;
308 $record = TLSProxy::Record->new(
310 TLSProxy::Record::RT_HANDSHAKE,
311 TLSProxy::Record::VERS_TLS_1_2,
319 push @{$proxy->record_list}, $record;
321 $fraglen = length $frag2;
323 if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
328 $record = TLSProxy::Record->new(
330 TLSProxy::Record::RT_HANDSHAKE,
331 TLSProxy::Record::VERS_TLS_1_2,
339 push @{$proxy->record_list}, $record;
341 $fraglen = length $frag3;
342 $record = TLSProxy::Record->new(
344 TLSProxy::Record::RT_HANDSHAKE,
345 TLSProxy::Record::VERS_TLS_1_2,
353 push @{$proxy->record_list}, $record;
358 sub add_unknown_record_type
362 # We'll change a record after the initial version neg has taken place
363 if ($proxy->flight != 2) {
367 my $lastrec = ${$proxy->record_list}[-1];
368 my $record = TLSProxy::Record->new(
370 TLSProxy::Record::RT_UNKNOWN,
380 unshift @{$proxy->record_list}, $record;