]>
Commit | Line | Data |
---|---|---|
4f0c4757 MC |
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 | ||
80f397e2 MC |
26 | plan skip_all => "$test_name needs TLSv1.2 enabled" |
27 | if disabled("tls1_2"); | |
4f0c4757 MC |
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 | ||
4f0c4757 MC |
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; | |
837e591d | 40 | $proxy->serverflags("-tls1_2"); |
b02b5743 | 41 | $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; |
a2a0c86b | 42 | plan tests => 9; |
4f0c4757 MC |
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; | |
837e591d | 48 | $proxy->serverflags("-tls1_2"); |
4f0c4757 MC |
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; | |
837e591d | 56 | $proxy->serverflags("-tls1_2"); |
4f0c4757 MC |
57 | $proxy->start(); |
58 | ok(TLSProxy::Message->fail(), "Too many in context empty records test"); | |
59 | ||
c3fd55d4 MC |
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); | |
837e591d | 65 | $proxy->serverflags("-tls1_2"); |
c3fd55d4 MC |
66 | $proxy->start(); |
67 | ok(!TLSProxy::Message->end(), "Fragmented alert records test"); | |
68 | ||
a2a0c86b MC |
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 | |
a2a0c86b MC |
79 | my $sslv2testtype = TLSV1_2_IN_SSLV2; |
80 | $proxy->clear(); | |
81 | $proxy->filter(\&add_sslv2_filter); | |
837e591d | 82 | $proxy->serverflags("-tls1_2"); |
a2a0c86b MC |
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(); | |
837e591d | 91 | $proxy->serverflags("-tls1_2"); |
a2a0c86b MC |
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(); | |
837e591d | 100 | $proxy->serverflags("-tls1_2"); |
a2a0c86b MC |
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(); | |
837e591d | 108 | $proxy->serverflags("-tls1_2"); |
a2a0c86b MC |
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(); | |
837e591d | 116 | $proxy->serverflags("-tls1_2"); |
a2a0c86b MC |
117 | $proxy->start(); |
118 | ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test"); | |
4f0c4757 MC |
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, | |
a2a0c86b | 136 | 0, |
4f0c4757 MC |
137 | "", |
138 | "" | |
139 | ); | |
140 | ||
141 | push @{$proxy->record_list}, $record; | |
142 | } | |
143 | } | |
c3fd55d4 MC |
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 | ||
60250017 | 168 | # Now add the alert level (Fatal) as a separate record |
c3fd55d4 MC |
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, | |
a2a0c86b | 175 | 0, |
c3fd55d4 MC |
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, | |
a2a0c86b | 190 | 0, |
c3fd55d4 MC |
191 | 1, |
192 | 1, | |
193 | $byte, | |
194 | $byte | |
195 | ); | |
196 | push @{$proxy->record_list}, $record; | |
197 | } | |
a2a0c86b MC |
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 | } |