8af236fa320cdba61e2ff3e4d9b79619e0c91d50
[openssl.git] / test / recipes / 70-test_sslrecords.t
1 #! /usr/bin/env perl
2 # Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
3 #
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
8
9 use strict;
10 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
11 use OpenSSL::Test::Utils;
12 use TLSProxy::Proxy;
13
14 my $test_name = "test_sslrecords";
15 setup($test_name);
16
17 plan skip_all => "TLSProxy isn't usable on $^O"
18     if $^O =~ /^(VMS|MSWin32)$/;
19
20 plan skip_all => "$test_name needs the dynamic engine feature enabled"
21     if disabled("engine") || disabled("dynamic-engine");
22
23 plan skip_all => "$test_name needs the sock feature enabled"
24     if disabled("sock");
25
26 plan skip_all => "$test_name needs TLSv1.2 enabled"
27     if disabled("tls1_2");
28
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})
35 );
36
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";
41 plan tests => 9;
42 ok(TLSProxy::Message->fail(), "Out of context empty records test");
43
44 #Test 2: Injecting in context empty records should succeed
45 $proxy->clear();
46 $content_type = TLSProxy::Record::RT_HANDSHAKE;
47 $proxy->start();
48 ok(TLSProxy::Message->success(), "In context empty records test");
49
50 #Test 3: Injecting too many in context empty records should fail
51 $proxy->clear();
52 #We allow 32 consecutive in context empty records
53 $inject_recs_num = 33;
54 $proxy->start();
55 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
56
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
60 $proxy->clear();
61 $proxy->filter(\&add_frag_alert_filter);
62 $proxy->start();
63 ok(!TLSProxy::Message->end(), "Fragmented alert records test");
64
65 #Run some SSLv2 ClientHello tests
66
67 use constant {
68     TLSV1_2_IN_SSLV2 => 0,
69     SSLV2_IN_SSLV2 => 1,
70     FRAGMENTED_IN_TLSV1_2 => 2,
71     FRAGMENTED_IN_SSLV2 => 3,
72     ALERT_BEFORE_SSLV2 => 4
73 };
74 #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
75 #my $sslv2testtype = TLSV1_2_IN_SSLV2;
76 my $sslv2testtype = TLSV1_2_IN_SSLV2;
77 $proxy->clear();
78 $proxy->filter(\&add_sslv2_filter);
79 $proxy->start();
80 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
81
82 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
83 #        support this so it should fail. We actually treat it as an unknown
84 #        protocol so we don't even send an alert in this case.
85 $sslv2testtype = SSLV2_IN_SSLV2;
86 $proxy->clear();
87 $proxy->start();
88 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
89
90 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
91 #        at all, but it gives us confidence that Test 8 fails for the right
92 #        reasons
93 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
94 $proxy->clear();
95 $proxy->start();
96 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
97
98 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
99 #        record; and another TLS1.2 record. This isn't allowed so should fail
100 $sslv2testtype = FRAGMENTED_IN_SSLV2;
101 $proxy->clear();
102 $proxy->start();
103 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
104
105 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
106 #        fail because an SSLv2 ClientHello must be the first record.
107 $sslv2testtype = ALERT_BEFORE_SSLV2;
108 $proxy->clear();
109 $proxy->start();
110 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
111 sub add_empty_recs_filter
112 {
113     my $proxy = shift;
114
115     # We're only interested in the initial ClientHello
116     if ($proxy->flight != 0) {
117         return;
118     }
119
120     for (my $i = 0; $i < $inject_recs_num; $i++) {
121         my $record = TLSProxy::Record->new(
122             0,
123             $content_type,
124             TLSProxy::Record::VERS_TLS_1_2,
125             0,
126             0,
127             0,
128             0,
129             "",
130             ""
131         );
132
133         push @{$proxy->record_list}, $record;
134     }
135 }
136
137 sub add_frag_alert_filter
138 {
139     my $proxy = shift;
140     my $byte;
141
142     # We're only interested in the initial ClientHello
143     if ($proxy->flight != 0) {
144         return;
145     }
146
147     # Add a zero length fragment first
148     #my $record = TLSProxy::Record->new(
149     #    0,
150     #    TLSProxy::Record::RT_ALERT,
151     #    TLSProxy::Record::VERS_TLS_1_2,
152     #    0,
153     #    0,
154     #    0,
155     #    "",
156     #    ""
157     #);
158     #push @{$proxy->record_list}, $record;
159
160     # Now add the alert level (Fatal) as a separate record
161     $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
162     my $record = TLSProxy::Record->new(
163         0,
164         TLSProxy::Record::RT_ALERT,
165         TLSProxy::Record::VERS_TLS_1_2,
166         1,
167         0,
168         1,
169         1,
170         $byte,
171         $byte
172     );
173     push @{$proxy->record_list}, $record;
174
175     # And finally the description (Unexpected message) in a third record
176     $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
177     $record = TLSProxy::Record->new(
178         0,
179         TLSProxy::Record::RT_ALERT,
180         TLSProxy::Record::VERS_TLS_1_2,
181         1,
182         0,
183         1,
184         1,
185         $byte,
186         $byte
187     );
188     push @{$proxy->record_list}, $record;
189 }
190
191 sub add_sslv2_filter
192 {
193     my $proxy = shift;
194     my $clienthello;
195     my $record;
196
197     # We're only interested in the initial ClientHello
198     if ($proxy->flight != 0) {
199         return;
200     }
201
202     # Ditch the real ClientHello - we're going to replace it with our own
203     shift @{$proxy->record_list};
204
205     if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
206         my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
207                                TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
208         my $alertlen = length $alert;
209         $record = TLSProxy::Record->new(
210             0,
211             TLSProxy::Record::RT_ALERT,
212             TLSProxy::Record::VERS_TLS_1_2,
213             $alertlen,
214             0,
215             $alertlen,
216             $alertlen,
217             $alert,
218             $alert
219         );
220
221         push @{$proxy->record_list}, $record;
222     }
223
224     if ($sslv2testtype == ALERT_BEFORE_SSLV2
225             || $sslv2testtype == TLSV1_2_IN_SSLV2
226             || $sslv2testtype == SSLV2_IN_SSLV2) {
227         # This is an SSLv2 format ClientHello
228         $clienthello =
229             pack "C44",
230             0x01, # ClientHello
231             0x03, 0x03, #TLSv1.2
232             0x00, 0x03, # Ciphersuites len
233             0x00, 0x00, # Session id len
234             0x00, 0x20, # Challenge len
235             0x00, 0x00, 0x2f, #AES128-SHA
236             0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
237             0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
238             0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
239
240         if ($sslv2testtype == SSLV2_IN_SSLV2) {
241             # Set the version to "real" SSLv2
242             vec($clienthello, 1, 8) = 0x00;
243             vec($clienthello, 2, 8) = 0x02;
244         }
245
246         my $chlen = length $clienthello;
247
248         $record = TLSProxy::Record->new(
249             0,
250             TLSProxy::Record::RT_HANDSHAKE,
251             TLSProxy::Record::VERS_TLS_1_2,
252             $chlen,
253             1, #SSLv2
254             $chlen,
255             $chlen,
256             $clienthello,
257             $clienthello
258         );
259
260         push @{$proxy->record_list}, $record;
261     } else {
262         # For this test we're using a real TLS ClientHello
263         $clienthello =
264             pack "C49",
265             0x01, # ClientHello
266             0x00, 0x00, 0x2D, # Message length
267             0x03, 0x03, # TLSv1.2
268             0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
269             0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
270             0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
271             0x00, # Session id len
272             0x00, 0x04, # Ciphersuites len
273             0x00, 0x2f, # AES128-SHA
274             0x00, 0xff, # Empty reneg info SCSV
275             0x01, # Compression methods len
276             0x00, # Null compression
277             0x00, 0x00; # Extensions len
278
279         # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
280         # We deliberately split the second record prior to the Challenge/Random
281         # and set the first byte of the random to 1. This makes the second SSLv2
282         # record look like an SSLv2 ClientHello
283         my $frag1 = substr $clienthello, 0, 6;
284         my $frag2 = substr $clienthello, 6, 32;
285         my $frag3 = substr $clienthello, 38;
286
287         my $fraglen = length $frag1;
288         $record = TLSProxy::Record->new(
289             0,
290             TLSProxy::Record::RT_HANDSHAKE,
291             TLSProxy::Record::VERS_TLS_1_2,
292             $fraglen,
293             0,
294             $fraglen,
295             $fraglen,
296             $frag1,
297             $frag1
298         );
299         push @{$proxy->record_list}, $record;
300
301         $fraglen = length $frag2;
302         my $recvers;
303         if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
304             $recvers = 1;
305         } else {
306             $recvers = 0;
307         }
308         $record = TLSProxy::Record->new(
309             0,
310             TLSProxy::Record::RT_HANDSHAKE,
311             TLSProxy::Record::VERS_TLS_1_2,
312             $fraglen,
313             $recvers,
314             $fraglen,
315             $fraglen,
316             $frag2,
317             $frag2
318         );
319         push @{$proxy->record_list}, $record;
320
321         $fraglen = length $frag3;
322         $record = TLSProxy::Record->new(
323             0,
324             TLSProxy::Record::RT_HANDSHAKE,
325             TLSProxy::Record::VERS_TLS_1_2,
326             $fraglen,
327             0,
328             $fraglen,
329             $fraglen,
330             $frag3,
331             $frag3
332         );
333         push @{$proxy->record_list}, $record;
334     }
335
336 }