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->serverflags("-tls1_2");
41 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
43 ok(TLSProxy::Message->fail(), "Out of context empty records test");
45 #Test 2: Injecting in context empty records should succeed
47 $content_type = TLSProxy::Record::RT_HANDSHAKE;
48 $proxy->serverflags("-tls1_2");
50 ok(TLSProxy::Message->success(), "In context empty records test");
52 #Test 3: Injecting too many in context empty records should fail
54 #We allow 32 consecutive in context empty records
55 $inject_recs_num = 33;
56 $proxy->serverflags("-tls1_2");
58 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
60 #Test 4: Injecting a fragmented fatal alert should fail. We actually expect no
61 # alerts to be sent from either side because *we* injected the fatal
62 # alert, i.e. this will look like a disorderly close
64 $proxy->filter(\&add_frag_alert_filter);
65 $proxy->serverflags("-tls1_2");
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);
82 $proxy->serverflags("-tls1_2");
84 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
86 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
87 # support this so it should fail. We actually treat it as an unknown
88 # protocol so we don't even send an alert in this case.
89 $sslv2testtype = SSLV2_IN_SSLV2;
91 $proxy->serverflags("-tls1_2");
93 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
95 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
96 # at all, but it gives us confidence that Test 8 fails for the right
98 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
100 $proxy->serverflags("-tls1_2");
102 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
104 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
105 # record; and another TLS1.2 record. This isn't allowed so should fail
106 $sslv2testtype = FRAGMENTED_IN_SSLV2;
108 $proxy->serverflags("-tls1_2");
110 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
112 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
113 # fail because an SSLv2 ClientHello must be the first record.
114 $sslv2testtype = ALERT_BEFORE_SSLV2;
116 $proxy->serverflags("-tls1_2");
118 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
119 sub add_empty_recs_filter
123 # We're only interested in the initial ClientHello
124 if ($proxy->flight != 0) {
128 for (my $i = 0; $i < $inject_recs_num; $i++) {
129 my $record = TLSProxy::Record->new(
132 TLSProxy::Record::VERS_TLS_1_2,
141 push @{$proxy->record_list}, $record;
145 sub add_frag_alert_filter
150 # We're only interested in the initial ClientHello
151 if ($proxy->flight != 0) {
155 # Add a zero length fragment first
156 #my $record = TLSProxy::Record->new(
158 # TLSProxy::Record::RT_ALERT,
159 # TLSProxy::Record::VERS_TLS_1_2,
166 #push @{$proxy->record_list}, $record;
168 # Now add the alert level (Fatal) as a separate record
169 $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
170 my $record = TLSProxy::Record->new(
172 TLSProxy::Record::RT_ALERT,
173 TLSProxy::Record::VERS_TLS_1_2,
181 push @{$proxy->record_list}, $record;
183 # And finally the description (Unexpected message) in a third record
184 $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
185 $record = TLSProxy::Record->new(
187 TLSProxy::Record::RT_ALERT,
188 TLSProxy::Record::VERS_TLS_1_2,
196 push @{$proxy->record_list}, $record;
205 # We're only interested in the initial ClientHello
206 if ($proxy->flight != 0) {
210 # Ditch the real ClientHello - we're going to replace it with our own
211 shift @{$proxy->record_list};
213 if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
214 my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
215 TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
216 my $alertlen = length $alert;
217 $record = TLSProxy::Record->new(
219 TLSProxy::Record::RT_ALERT,
220 TLSProxy::Record::VERS_TLS_1_2,
229 push @{$proxy->record_list}, $record;
232 if ($sslv2testtype == ALERT_BEFORE_SSLV2
233 || $sslv2testtype == TLSV1_2_IN_SSLV2
234 || $sslv2testtype == SSLV2_IN_SSLV2) {
235 # This is an SSLv2 format ClientHello
240 0x00, 0x03, # Ciphersuites len
241 0x00, 0x00, # Session id len
242 0x00, 0x20, # Challenge len
243 0x00, 0x00, 0x2f, #AES128-SHA
244 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
245 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
246 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
248 if ($sslv2testtype == SSLV2_IN_SSLV2) {
249 # Set the version to "real" SSLv2
250 vec($clienthello, 1, 8) = 0x00;
251 vec($clienthello, 2, 8) = 0x02;
254 my $chlen = length $clienthello;
256 $record = TLSProxy::Record->new(
258 TLSProxy::Record::RT_HANDSHAKE,
259 TLSProxy::Record::VERS_TLS_1_2,
268 push @{$proxy->record_list}, $record;
270 # For this test we're using a real TLS ClientHello
274 0x00, 0x00, 0x2D, # Message length
275 0x03, 0x03, # TLSv1.2
276 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
277 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
278 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
279 0x00, # Session id len
280 0x00, 0x04, # Ciphersuites len
281 0x00, 0x2f, # AES128-SHA
282 0x00, 0xff, # Empty reneg info SCSV
283 0x01, # Compression methods len
284 0x00, # Null compression
285 0x00, 0x00; # Extensions len
287 # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
288 # We deliberately split the second record prior to the Challenge/Random
289 # and set the first byte of the random to 1. This makes the second SSLv2
290 # record look like an SSLv2 ClientHello
291 my $frag1 = substr $clienthello, 0, 6;
292 my $frag2 = substr $clienthello, 6, 32;
293 my $frag3 = substr $clienthello, 38;
295 my $fraglen = length $frag1;
296 $record = TLSProxy::Record->new(
298 TLSProxy::Record::RT_HANDSHAKE,
299 TLSProxy::Record::VERS_TLS_1_2,
307 push @{$proxy->record_list}, $record;
309 $fraglen = length $frag2;
311 if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
316 $record = TLSProxy::Record->new(
318 TLSProxy::Record::RT_HANDSHAKE,
319 TLSProxy::Record::VERS_TLS_1_2,
327 push @{$proxy->record_list}, $record;
329 $fraglen = length $frag3;
330 $record = TLSProxy::Record->new(
332 TLSProxy::Record::RT_HANDSHAKE,
333 TLSProxy::Record::VERS_TLS_1_2,
341 push @{$proxy->record_list}, $record;