Fail if an unrecognised record type is received
[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->serverflags("-tls1_2");
41 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
42 plan tests => 9;
43 ok(TLSProxy::Message->fail(), "Out of context empty records test");
44
45 #Test 2: Injecting in context empty records should succeed
46 $proxy->clear();
47 $content_type = TLSProxy::Record::RT_HANDSHAKE;
48 $proxy->serverflags("-tls1_2");
49 $proxy->start();
50 ok(TLSProxy::Message->success(), "In context empty records test");
51
52 #Test 3: Injecting too many in context empty records should fail
53 $proxy->clear();
54 #We allow 32 consecutive in context empty records
55 $inject_recs_num = 33;
56 $proxy->serverflags("-tls1_2");
57 $proxy->start();
58 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
59
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
63 $proxy->clear();
64 $proxy->filter(\&add_frag_alert_filter);
65 $proxy->serverflags("-tls1_2");
66 $proxy->start();
67 ok(!TLSProxy::Message->end(), "Fragmented alert records test");
68
69 #Run some SSLv2 ClientHello tests
70
71 use constant {
72     TLSV1_2_IN_SSLV2 => 0,
73     SSLV2_IN_SSLV2 => 1,
74     FRAGMENTED_IN_TLSV1_2 => 2,
75     FRAGMENTED_IN_SSLV2 => 3,
76     ALERT_BEFORE_SSLV2 => 4
77 };
78 #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
79 my $sslv2testtype = TLSV1_2_IN_SSLV2;
80 $proxy->clear();
81 $proxy->filter(\&add_sslv2_filter);
82 $proxy->serverflags("-tls1_2");
83 $proxy->start();
84 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
85
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;
90 $proxy->clear();
91 $proxy->serverflags("-tls1_2");
92 $proxy->start();
93 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
94
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
97 #        reasons
98 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
99 $proxy->clear();
100 $proxy->serverflags("-tls1_2");
101 $proxy->start();
102 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
103
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;
107 $proxy->clear();
108 $proxy->serverflags("-tls1_2");
109 $proxy->start();
110 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
111
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;
115 $proxy->clear();
116 $proxy->serverflags("-tls1_2");
117 $proxy->start();
118 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
119 sub add_empty_recs_filter
120 {
121     my $proxy = shift;
122
123     # We're only interested in the initial ClientHello
124     if ($proxy->flight != 0) {
125         return;
126     }
127
128     for (my $i = 0; $i < $inject_recs_num; $i++) {
129         my $record = TLSProxy::Record->new(
130             0,
131             $content_type,
132             TLSProxy::Record::VERS_TLS_1_2,
133             0,
134             0,
135             0,
136             0,
137             "",
138             ""
139         );
140
141         push @{$proxy->record_list}, $record;
142     }
143 }
144
145 sub add_frag_alert_filter
146 {
147     my $proxy = shift;
148     my $byte;
149
150     # We're only interested in the initial ClientHello
151     if ($proxy->flight != 0) {
152         return;
153     }
154
155     # Add a zero length fragment first
156     #my $record = TLSProxy::Record->new(
157     #    0,
158     #    TLSProxy::Record::RT_ALERT,
159     #    TLSProxy::Record::VERS_TLS_1_2,
160     #    0,
161     #    0,
162     #    0,
163     #    "",
164     #    ""
165     #);
166     #push @{$proxy->record_list}, $record;
167
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(
171         0,
172         TLSProxy::Record::RT_ALERT,
173         TLSProxy::Record::VERS_TLS_1_2,
174         1,
175         0,
176         1,
177         1,
178         $byte,
179         $byte
180     );
181     push @{$proxy->record_list}, $record;
182
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(
186         0,
187         TLSProxy::Record::RT_ALERT,
188         TLSProxy::Record::VERS_TLS_1_2,
189         1,
190         0,
191         1,
192         1,
193         $byte,
194         $byte
195     );
196     push @{$proxy->record_list}, $record;
197 }
198
199 sub add_sslv2_filter
200 {
201     my $proxy = shift;
202     my $clienthello;
203     my $record;
204
205     # We're only interested in the initial ClientHello
206     if ($proxy->flight != 0) {
207         return;
208     }
209
210     # Ditch the real ClientHello - we're going to replace it with our own
211     shift @{$proxy->record_list};
212
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(
218             0,
219             TLSProxy::Record::RT_ALERT,
220             TLSProxy::Record::VERS_TLS_1_2,
221             $alertlen,
222             0,
223             $alertlen,
224             $alertlen,
225             $alert,
226             $alert
227         );
228
229         push @{$proxy->record_list}, $record;
230     }
231
232     if ($sslv2testtype == ALERT_BEFORE_SSLV2
233             || $sslv2testtype == TLSV1_2_IN_SSLV2
234             || $sslv2testtype == SSLV2_IN_SSLV2) {
235         # This is an SSLv2 format ClientHello
236         $clienthello =
237             pack "C44",
238             0x01, # ClientHello
239             0x03, 0x03, #TLSv1.2
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
247
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;
252         }
253
254         my $chlen = length $clienthello;
255
256         $record = TLSProxy::Record->new(
257             0,
258             TLSProxy::Record::RT_HANDSHAKE,
259             TLSProxy::Record::VERS_TLS_1_2,
260             $chlen,
261             1, #SSLv2
262             $chlen,
263             $chlen,
264             $clienthello,
265             $clienthello
266         );
267
268         push @{$proxy->record_list}, $record;
269     } else {
270         # For this test we're using a real TLS ClientHello
271         $clienthello =
272             pack "C49",
273             0x01, # 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
286
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;
294
295         my $fraglen = length $frag1;
296         $record = TLSProxy::Record->new(
297             0,
298             TLSProxy::Record::RT_HANDSHAKE,
299             TLSProxy::Record::VERS_TLS_1_2,
300             $fraglen,
301             0,
302             $fraglen,
303             $fraglen,
304             $frag1,
305             $frag1
306         );
307         push @{$proxy->record_list}, $record;
308
309         $fraglen = length $frag2;
310         my $recvers;
311         if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
312             $recvers = 1;
313         } else {
314             $recvers = 0;
315         }
316         $record = TLSProxy::Record->new(
317             0,
318             TLSProxy::Record::RT_HANDSHAKE,
319             TLSProxy::Record::VERS_TLS_1_2,
320             $fraglen,
321             $recvers,
322             $fraglen,
323             $fraglen,
324             $frag2,
325             $frag2
326         );
327         push @{$proxy->record_list}, $record;
328
329         $fraglen = length $frag3;
330         $record = TLSProxy::Record->new(
331             0,
332             TLSProxy::Record::RT_HANDSHAKE,
333             TLSProxy::Record::VERS_TLS_1_2,
334             $fraglen,
335             0,
336             $fraglen,
337             $fraglen,
338             $frag3,
339             $frag3
340         );
341         push @{$proxy->record_list}, $record;
342     }
343
344 }