d1c8d3ad2ac1d62d223333be432edfe531880147
[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 $proxy->clear();
77 $proxy->filter(\&add_sslv2_filter);
78 $proxy->start();
79 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
80
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;
85 $proxy->clear();
86 $proxy->start();
87 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
88
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
91 #        reasons
92 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
93 $proxy->clear();
94 $proxy->start();
95 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
96
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;
100 $proxy->clear();
101 $proxy->start();
102 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
103
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;
107 $proxy->clear();
108 $proxy->start();
109 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
110 sub add_empty_recs_filter
111 {
112     my $proxy = shift;
113
114     # We're only interested in the initial ClientHello
115     if ($proxy->flight != 0) {
116         return;
117     }
118
119     for (my $i = 0; $i < $inject_recs_num; $i++) {
120         my $record = TLSProxy::Record->new(
121             0,
122             $content_type,
123             TLSProxy::Record::VERS_TLS_1_2,
124             0,
125             0,
126             0,
127             0,
128             "",
129             ""
130         );
131
132         push @{$proxy->record_list}, $record;
133     }
134 }
135
136 sub add_frag_alert_filter
137 {
138     my $proxy = shift;
139     my $byte;
140
141     # We're only interested in the initial ClientHello
142     if ($proxy->flight != 0) {
143         return;
144     }
145
146     # Add a zero length fragment first
147     #my $record = TLSProxy::Record->new(
148     #    0,
149     #    TLSProxy::Record::RT_ALERT,
150     #    TLSProxy::Record::VERS_TLS_1_2,
151     #    0,
152     #    0,
153     #    0,
154     #    "",
155     #    ""
156     #);
157     #push @{$proxy->record_list}, $record;
158
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(
162         0,
163         TLSProxy::Record::RT_ALERT,
164         TLSProxy::Record::VERS_TLS_1_2,
165         1,
166         0,
167         1,
168         1,
169         $byte,
170         $byte
171     );
172     push @{$proxy->record_list}, $record;
173
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(
177         0,
178         TLSProxy::Record::RT_ALERT,
179         TLSProxy::Record::VERS_TLS_1_2,
180         1,
181         0,
182         1,
183         1,
184         $byte,
185         $byte
186     );
187     push @{$proxy->record_list}, $record;
188 }
189
190 sub add_sslv2_filter
191 {
192     my $proxy = shift;
193     my $clienthello;
194     my $record;
195
196     # We're only interested in the initial ClientHello
197     if ($proxy->flight != 0) {
198         return;
199     }
200
201     # Ditch the real ClientHello - we're going to replace it with our own
202     shift @{$proxy->record_list};
203
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(
209             0,
210             TLSProxy::Record::RT_ALERT,
211             TLSProxy::Record::VERS_TLS_1_2,
212             $alertlen,
213             0,
214             $alertlen,
215             $alertlen,
216             $alert,
217             $alert
218         );
219
220         push @{$proxy->record_list}, $record;
221     }
222
223     if ($sslv2testtype == ALERT_BEFORE_SSLV2
224             || $sslv2testtype == TLSV1_2_IN_SSLV2
225             || $sslv2testtype == SSLV2_IN_SSLV2) {
226         # This is an SSLv2 format ClientHello
227         $clienthello =
228             pack "C44",
229             0x01, # ClientHello
230             0x03, 0x03, #TLSv1.2
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
238
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;
243         }
244
245         my $chlen = length $clienthello;
246
247         $record = TLSProxy::Record->new(
248             0,
249             TLSProxy::Record::RT_HANDSHAKE,
250             TLSProxy::Record::VERS_TLS_1_2,
251             $chlen,
252             1, #SSLv2
253             $chlen,
254             $chlen,
255             $clienthello,
256             $clienthello
257         );
258
259         push @{$proxy->record_list}, $record;
260     } else {
261         # For this test we're using a real TLS ClientHello
262         $clienthello =
263             pack "C49",
264             0x01, # 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
277
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;
285
286         my $fraglen = length $frag1;
287         $record = TLSProxy::Record->new(
288             0,
289             TLSProxy::Record::RT_HANDSHAKE,
290             TLSProxy::Record::VERS_TLS_1_2,
291             $fraglen,
292             0,
293             $fraglen,
294             $fraglen,
295             $frag1,
296             $frag1
297         );
298         push @{$proxy->record_list}, $record;
299
300         $fraglen = length $frag2;
301         my $recvers;
302         if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
303             $recvers = 1;
304         } else {
305             $recvers = 0;
306         }
307         $record = TLSProxy::Record->new(
308             0,
309             TLSProxy::Record::RT_HANDSHAKE,
310             TLSProxy::Record::VERS_TLS_1_2,
311             $fraglen,
312             $recvers,
313             $fraglen,
314             $fraglen,
315             $frag2,
316             $frag2
317         );
318         push @{$proxy->record_list}, $record;
319
320         $fraglen = length $frag3;
321         $record = TLSProxy::Record->new(
322             0,
323             TLSProxy::Record::RT_HANDSHAKE,
324             TLSProxy::Record::VERS_TLS_1_2,
325             $fraglen,
326             0,
327             $fraglen,
328             $fraglen,
329             $frag3,
330             $frag3
331         );
332         push @{$proxy->record_list}, $record;
333     }
334
335 }