CRL critical extension bugfix
[oweals/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 my $num_tests = 10;
42 if (!disabled("tls1_1")) {
43     $num_tests++;
44 }
45 plan tests => $num_tests;
46 ok(TLSProxy::Message->fail(), "Out of context empty records test");
47
48 #Test 2: Injecting in context empty records should succeed
49 $proxy->clear();
50 $content_type = TLSProxy::Record::RT_HANDSHAKE;
51 $proxy->start();
52 ok(TLSProxy::Message->success(), "In context empty records test");
53
54 #Test 3: Injecting too many in context empty records should fail
55 $proxy->clear();
56 #We allow 32 consecutive in context empty records
57 $inject_recs_num = 33;
58 $proxy->start();
59 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
60
61 #Test 4: Injecting a fragmented fatal alert should fail. We actually expect no
62 #        alerts to be sent from either side because *we* injected the fatal
63 #        alert, i.e. this will look like a disorderly close
64 $proxy->clear();
65 $proxy->filter(\&add_frag_alert_filter);
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->start();
83 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
84
85 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
86 #        support this so it should fail. We actually treat it as an unknown
87 #        protocol so we don't even send an alert in this case.
88 $sslv2testtype = SSLV2_IN_SSLV2;
89 $proxy->clear();
90 $proxy->start();
91 ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
92
93 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
94 #        at all, but it gives us confidence that Test 8 fails for the right
95 #        reasons
96 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
97 $proxy->clear();
98 $proxy->start();
99 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
100
101 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
102 #        record; and another TLS1.2 record. This isn't allowed so should fail
103 $sslv2testtype = FRAGMENTED_IN_SSLV2;
104 $proxy->clear();
105 $proxy->start();
106 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
107
108 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
109 #        fail because an SSLv2 ClientHello must be the first record.
110 $sslv2testtype = ALERT_BEFORE_SSLV2;
111 $proxy->clear();
112 $proxy->start();
113 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
114
115 #Unregcognised record type tests
116
117 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
118 $proxy->clear();
119 $proxy->filter(\&add_unknown_record_type);
120 $proxy->start();
121 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
122
123 #Test 11: Sending an unrecognised record type in TLS1.1 should fail
124 if (!disabled("tls1_1")) {
125     $proxy->clear();
126     $proxy->clientflags("-tls1_1");
127     $proxy->start();
128     ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
129 }
130
131 sub add_empty_recs_filter
132 {
133     my $proxy = shift;
134
135     # We're only interested in the initial ClientHello
136     if ($proxy->flight != 0) {
137         return;
138     }
139
140     for (my $i = 0; $i < $inject_recs_num; $i++) {
141         my $record = TLSProxy::Record->new(
142             0,
143             $content_type,
144             TLSProxy::Record::VERS_TLS_1_2,
145             0,
146             0,
147             0,
148             0,
149             "",
150             ""
151         );
152
153         push @{$proxy->record_list}, $record;
154     }
155 }
156
157 sub add_frag_alert_filter
158 {
159     my $proxy = shift;
160     my $byte;
161
162     # We're only interested in the initial ClientHello
163     if ($proxy->flight != 0) {
164         return;
165     }
166
167     # Add a zero length fragment first
168     #my $record = TLSProxy::Record->new(
169     #    0,
170     #    TLSProxy::Record::RT_ALERT,
171     #    TLSProxy::Record::VERS_TLS_1_2,
172     #    0,
173     #    0,
174     #    0,
175     #    "",
176     #    ""
177     #);
178     #push @{$proxy->record_list}, $record;
179
180     # Now add the alert level (Fatal) as a separate record
181     $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
182     my $record = TLSProxy::Record->new(
183         0,
184         TLSProxy::Record::RT_ALERT,
185         TLSProxy::Record::VERS_TLS_1_2,
186         1,
187         0,
188         1,
189         1,
190         $byte,
191         $byte
192     );
193     push @{$proxy->record_list}, $record;
194
195     # And finally the description (Unexpected message) in a third record
196     $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
197     $record = TLSProxy::Record->new(
198         0,
199         TLSProxy::Record::RT_ALERT,
200         TLSProxy::Record::VERS_TLS_1_2,
201         1,
202         0,
203         1,
204         1,
205         $byte,
206         $byte
207     );
208     push @{$proxy->record_list}, $record;
209 }
210
211 sub add_sslv2_filter
212 {
213     my $proxy = shift;
214     my $clienthello;
215     my $record;
216
217     # We're only interested in the initial ClientHello
218     if ($proxy->flight != 0) {
219         return;
220     }
221
222     # Ditch the real ClientHello - we're going to replace it with our own
223     shift @{$proxy->record_list};
224
225     if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
226         my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
227                                TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
228         my $alertlen = length $alert;
229         $record = TLSProxy::Record->new(
230             0,
231             TLSProxy::Record::RT_ALERT,
232             TLSProxy::Record::VERS_TLS_1_2,
233             $alertlen,
234             0,
235             $alertlen,
236             $alertlen,
237             $alert,
238             $alert
239         );
240
241         push @{$proxy->record_list}, $record;
242     }
243
244     if ($sslv2testtype == ALERT_BEFORE_SSLV2
245             || $sslv2testtype == TLSV1_2_IN_SSLV2
246             || $sslv2testtype == SSLV2_IN_SSLV2) {
247         # This is an SSLv2 format ClientHello
248         $clienthello =
249             pack "C44",
250             0x01, # ClientHello
251             0x03, 0x03, #TLSv1.2
252             0x00, 0x03, # Ciphersuites len
253             0x00, 0x00, # Session id len
254             0x00, 0x20, # Challenge len
255             0x00, 0x00, 0x2f, #AES128-SHA
256             0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
257             0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
258             0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
259
260         if ($sslv2testtype == SSLV2_IN_SSLV2) {
261             # Set the version to "real" SSLv2
262             vec($clienthello, 1, 8) = 0x00;
263             vec($clienthello, 2, 8) = 0x02;
264         }
265
266         my $chlen = length $clienthello;
267
268         $record = TLSProxy::Record->new(
269             0,
270             TLSProxy::Record::RT_HANDSHAKE,
271             TLSProxy::Record::VERS_TLS_1_2,
272             $chlen,
273             1, #SSLv2
274             $chlen,
275             $chlen,
276             $clienthello,
277             $clienthello
278         );
279
280         push @{$proxy->record_list}, $record;
281     } else {
282         # For this test we're using a real TLS ClientHello
283         $clienthello =
284             pack "C49",
285             0x01, # ClientHello
286             0x00, 0x00, 0x2D, # Message length
287             0x03, 0x03, # TLSv1.2
288             0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
289             0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
290             0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
291             0x00, # Session id len
292             0x00, 0x04, # Ciphersuites len
293             0x00, 0x2f, # AES128-SHA
294             0x00, 0xff, # Empty reneg info SCSV
295             0x01, # Compression methods len
296             0x00, # Null compression
297             0x00, 0x00; # Extensions len
298
299         # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
300         # We deliberately split the second record prior to the Challenge/Random
301         # and set the first byte of the random to 1. This makes the second SSLv2
302         # record look like an SSLv2 ClientHello
303         my $frag1 = substr $clienthello, 0, 6;
304         my $frag2 = substr $clienthello, 6, 32;
305         my $frag3 = substr $clienthello, 38;
306
307         my $fraglen = length $frag1;
308         $record = TLSProxy::Record->new(
309             0,
310             TLSProxy::Record::RT_HANDSHAKE,
311             TLSProxy::Record::VERS_TLS_1_2,
312             $fraglen,
313             0,
314             $fraglen,
315             $fraglen,
316             $frag1,
317             $frag1
318         );
319         push @{$proxy->record_list}, $record;
320
321         $fraglen = length $frag2;
322         my $recvers;
323         if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
324             $recvers = 1;
325         } else {
326             $recvers = 0;
327         }
328         $record = TLSProxy::Record->new(
329             0,
330             TLSProxy::Record::RT_HANDSHAKE,
331             TLSProxy::Record::VERS_TLS_1_2,
332             $fraglen,
333             $recvers,
334             $fraglen,
335             $fraglen,
336             $frag2,
337             $frag2
338         );
339         push @{$proxy->record_list}, $record;
340
341         $fraglen = length $frag3;
342         $record = TLSProxy::Record->new(
343             0,
344             TLSProxy::Record::RT_HANDSHAKE,
345             TLSProxy::Record::VERS_TLS_1_2,
346             $fraglen,
347             0,
348             $fraglen,
349             $fraglen,
350             $frag3,
351             $frag3
352         );
353         push @{$proxy->record_list}, $record;
354     }
355
356 }
357
358 sub add_unknown_record_type
359 {
360     my $proxy = shift;
361
362     # We'll change a record after the initial version neg has taken place
363     if ($proxy->flight != 2) {
364         return;
365     }
366
367     my $lastrec = ${$proxy->record_list}[-1];
368     my $record = TLSProxy::Record->new(
369         2,
370         TLSProxy::Record::RT_UNKNOWN,
371         $lastrec->version(),
372         1,
373         0,
374         1,
375         1,
376         "X",
377         "X"
378     );
379
380     unshift @{$proxy->record_list}, $record;
381 }