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 ok(TLSProxy::Message->fail(), "Out of context empty records test");
44 #Test 2: Injecting in context empty records should succeed
46 $content_type = TLSProxy::Record::RT_HANDSHAKE;
48 ok(TLSProxy::Message->success(), "In context empty records test");
50 #Test 3: Injecting too many in context empty records should fail
52 #We allow 32 consecutive in context empty records
53 $inject_recs_num = 33;
55 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
57 #Test 4: Injecting a fragmented fatal alert should fail. We actually expect no
58 # alerts to be sent from either side because *we* injected the fatal
59 # alert, i.e. this will look like a disorderly close
61 $proxy->filter(\&add_frag_alert_filter);
63 ok(!TLSProxy::Message->end(), "Fragmented alert records test");
65 #Run some SSLv2 ClientHello tests
68 TLSV1_2_IN_SSLV2 => 0,
70 FRAGMENTED_IN_TLSV1_2 => 2,
71 FRAGMENTED_IN_SSLV2 => 3,
72 ALERT_BEFORE_SSLV2 => 4
74 #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
75 my $sslv2testtype = TLSV1_2_IN_SSLV2;
77 $proxy->filter(\&add_sslv2_filter);
79 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
81 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
82 # support this so it should fail. We actually treat it as an unknown
83 # protocol so we don't even send an alert in this case.
84 $sslv2testtype = SSLV2_IN_SSLV2;
87 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
89 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
90 # at all, but it gives us confidence that Test 8 fails for the right
92 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
95 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
97 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
98 # record; and another TLS1.2 record. This isn't allowed so should fail
99 $sslv2testtype = FRAGMENTED_IN_SSLV2;
102 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
104 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
105 # fail because an SSLv2 ClientHello must be the first record.
106 $sslv2testtype = ALERT_BEFORE_SSLV2;
109 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
110 sub add_empty_recs_filter
114 # We're only interested in the initial ClientHello
115 if ($proxy->flight != 0) {
119 for (my $i = 0; $i < $inject_recs_num; $i++) {
120 my $record = TLSProxy::Record->new(
123 TLSProxy::Record::VERS_TLS_1_2,
132 push @{$proxy->record_list}, $record;
136 sub add_frag_alert_filter
141 # We're only interested in the initial ClientHello
142 if ($proxy->flight != 0) {
146 # Add a zero length fragment first
147 #my $record = TLSProxy::Record->new(
149 # TLSProxy::Record::RT_ALERT,
150 # TLSProxy::Record::VERS_TLS_1_2,
157 #push @{$proxy->record_list}, $record;
159 # Now add the alert level (Fatal) as a separate record
160 $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
161 my $record = TLSProxy::Record->new(
163 TLSProxy::Record::RT_ALERT,
164 TLSProxy::Record::VERS_TLS_1_2,
172 push @{$proxy->record_list}, $record;
174 # And finally the description (Unexpected message) in a third record
175 $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
176 $record = TLSProxy::Record->new(
178 TLSProxy::Record::RT_ALERT,
179 TLSProxy::Record::VERS_TLS_1_2,
187 push @{$proxy->record_list}, $record;
196 # We're only interested in the initial ClientHello
197 if ($proxy->flight != 0) {
201 # Ditch the real ClientHello - we're going to replace it with our own
202 shift @{$proxy->record_list};
204 if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
205 my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
206 TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
207 my $alertlen = length $alert;
208 $record = TLSProxy::Record->new(
210 TLSProxy::Record::RT_ALERT,
211 TLSProxy::Record::VERS_TLS_1_2,
220 push @{$proxy->record_list}, $record;
223 if ($sslv2testtype == ALERT_BEFORE_SSLV2
224 || $sslv2testtype == TLSV1_2_IN_SSLV2
225 || $sslv2testtype == SSLV2_IN_SSLV2) {
226 # This is an SSLv2 format ClientHello
231 0x00, 0x03, # Ciphersuites len
232 0x00, 0x00, # Session id len
233 0x00, 0x20, # Challenge len
234 0x00, 0x00, 0x2f, #AES128-SHA
235 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
236 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
237 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
239 if ($sslv2testtype == SSLV2_IN_SSLV2) {
240 # Set the version to "real" SSLv2
241 vec($clienthello, 1, 8) = 0x00;
242 vec($clienthello, 2, 8) = 0x02;
245 my $chlen = length $clienthello;
247 $record = TLSProxy::Record->new(
249 TLSProxy::Record::RT_HANDSHAKE,
250 TLSProxy::Record::VERS_TLS_1_2,
259 push @{$proxy->record_list}, $record;
261 # For this test we're using a real TLS ClientHello
265 0x00, 0x00, 0x2D, # Message length
266 0x03, 0x03, # TLSv1.2
267 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
268 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
269 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
270 0x00, # Session id len
271 0x00, 0x04, # Ciphersuites len
272 0x00, 0x2f, # AES128-SHA
273 0x00, 0xff, # Empty reneg info SCSV
274 0x01, # Compression methods len
275 0x00, # Null compression
276 0x00, 0x00; # Extensions len
278 # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
279 # We deliberately split the second record prior to the Challenge/Random
280 # and set the first byte of the random to 1. This makes the second SSLv2
281 # record look like an SSLv2 ClientHello
282 my $frag1 = substr $clienthello, 0, 6;
283 my $frag2 = substr $clienthello, 6, 32;
284 my $frag3 = substr $clienthello, 38;
286 my $fraglen = length $frag1;
287 $record = TLSProxy::Record->new(
289 TLSProxy::Record::RT_HANDSHAKE,
290 TLSProxy::Record::VERS_TLS_1_2,
298 push @{$proxy->record_list}, $record;
300 $fraglen = length $frag2;
302 if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
307 $record = TLSProxy::Record->new(
309 TLSProxy::Record::RT_HANDSHAKE,
310 TLSProxy::Record::VERS_TLS_1_2,
318 push @{$proxy->record_list}, $record;
320 $fraglen = length $frag3;
321 $record = TLSProxy::Record->new(
323 TLSProxy::Record::RT_HANDSHAKE,
324 TLSProxy::Record::VERS_TLS_1_2,
332 push @{$proxy->record_list}, $record;