]>
Commit | Line | Data |
---|---|---|
631c1206 MC |
1 | # Written by Matt Caswell for the OpenSSL project. |
2 | # ==================================================================== | |
3 | # Copyright (c) 1998-2015 The OpenSSL Project. All rights reserved. | |
4 | # | |
5 | # Redistribution and use in source and binary forms, with or without | |
6 | # modification, are permitted provided that the following conditions | |
7 | # are met: | |
8 | # | |
9 | # 1. Redistributions of source code must retain the above copyright | |
10 | # notice, this list of conditions and the following disclaimer. | |
11 | # | |
12 | # 2. Redistributions in binary form must reproduce the above copyright | |
13 | # notice, this list of conditions and the following disclaimer in | |
14 | # the documentation and/or other materials provided with the | |
15 | # distribution. | |
16 | # | |
17 | # 3. All advertising materials mentioning features or use of this | |
18 | # software must display the following acknowledgment: | |
19 | # "This product includes software developed by the OpenSSL Project | |
20 | # for use in the OpenSSL Toolkit. (http://www.openssl.org/)" | |
21 | # | |
22 | # 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to | |
23 | # endorse or promote products derived from this software without | |
24 | # prior written permission. For written permission, please contact | |
25 | # openssl-core@openssl.org. | |
26 | # | |
27 | # 5. Products derived from this software may not be called "OpenSSL" | |
28 | # nor may "OpenSSL" appear in their names without prior written | |
29 | # permission of the OpenSSL Project. | |
30 | # | |
31 | # 6. Redistributions of any form whatsoever must retain the following | |
32 | # acknowledgment: | |
33 | # "This product includes software developed by the OpenSSL Project | |
34 | # for use in the OpenSSL Toolkit (http://www.openssl.org/)" | |
35 | # | |
36 | # THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY | |
37 | # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
38 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
39 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR | |
40 | # ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
41 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
42 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
43 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
44 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
45 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
46 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
47 | # OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 | # ==================================================================== | |
49 | # | |
50 | # This product includes cryptographic software written by Eric Young | |
51 | # (eay@cryptsoft.com). This product includes software written by Tim | |
52 | # Hudson (tjh@cryptsoft.com). | |
53 | ||
54 | use strict; | |
55 | ||
56 | package TLSProxy::Message; | |
57 | ||
58 | use constant TLS_MESSAGE_HEADER_LENGTH => 4; | |
59 | ||
60 | #Message types | |
61 | use constant { | |
62 | MT_HELLO_REQUEST => 0, | |
63 | MT_CLIENT_HELLO => 1, | |
64 | MT_SERVER_HELLO => 2, | |
65 | MT_NEW_SESSION_TICKET => 4, | |
66 | MT_CERTIFICATE => 11, | |
67 | MT_SERVER_KEY_EXCHANGE => 12, | |
68 | MT_CERTIFICATE_REQUEST => 13, | |
69 | MT_SERVER_HELLO_DONE => 14, | |
70 | MT_CERTIFICATE_VERIFY => 15, | |
71 | MT_CLIENT_KEY_EXCHANGE => 16, | |
72 | MT_FINISHED => 20, | |
73 | MT_CERTIFICATE_STATUS => 22, | |
74 | MT_NEXT_PROTO => 67 | |
75 | }; | |
76 | my %message_type = ( | |
77 | MT_HELLO_REQUEST, "HelloRequest", | |
78 | MT_CLIENT_HELLO, "ClientHello", | |
79 | MT_SERVER_HELLO, "ServerHello", | |
80 | MT_NEW_SESSION_TICKET, "NewSessionTicket", | |
81 | MT_CERTIFICATE, "Certificate", | |
82 | MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange", | |
83 | MT_CERTIFICATE_REQUEST, "CertificateRequest", | |
84 | MT_SERVER_HELLO_DONE, "ServerHelloDone", | |
85 | MT_CERTIFICATE_VERIFY, "CertificateVerify", | |
86 | MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange", | |
87 | MT_FINISHED, "Finished", | |
88 | MT_CERTIFICATE_STATUS, "CertificateStatus", | |
89 | MT_NEXT_PROTO, "NextProto" | |
90 | ); | |
91 | ||
92 | my $payload = ""; | |
93 | my $messlen = -1; | |
94 | my $mt; | |
95 | my $startoffset = -1; | |
96 | my $server = 0; | |
97 | my $success = 0; | |
98 | my $end = 0; | |
99 | my @message_rec_list = (); | |
100 | my @message_frag_lens = (); | |
a1accbb1 | 101 | my $ciphersuite = 0; |
631c1206 MC |
102 | |
103 | sub clear | |
104 | { | |
105 | $payload = ""; | |
106 | $messlen = -1; | |
107 | $startoffset = -1; | |
108 | $server = 0; | |
109 | $success = 0; | |
110 | $end = 0; | |
111 | @message_rec_list = (); | |
112 | @message_frag_lens = (); | |
113 | } | |
114 | ||
115 | #Class method to extract messages from a record | |
116 | sub get_messages | |
117 | { | |
118 | my $class = shift; | |
119 | my $serverin = shift; | |
120 | my $record = shift; | |
121 | my @messages = (); | |
122 | my $message; | |
123 | ||
a1accbb1 MC |
124 | @message_frag_lens = (); |
125 | ||
631c1206 MC |
126 | if ($serverin != $server && length($payload) != 0) { |
127 | die "Changed peer, but we still have fragment data\n"; | |
128 | } | |
129 | $server = $serverin; | |
130 | ||
131 | if ($record->content_type == TLSProxy::Record::RT_CCS) { | |
132 | if ($payload ne "") { | |
133 | #We can't handle this yet | |
134 | die "CCS received before message data complete\n"; | |
135 | } | |
136 | if ($server) { | |
137 | TLSProxy::Record->server_ccs_seen(1); | |
138 | } else { | |
139 | TLSProxy::Record->client_ccs_seen(1); | |
140 | } | |
141 | } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) { | |
142 | if ($record->len == 0 || $record->len_real == 0) { | |
143 | print " Message truncated\n"; | |
144 | } else { | |
145 | my $recoffset = 0; | |
146 | ||
147 | if (length $payload > 0) { | |
148 | #We are continuing processing a message started in a previous | |
149 | #record. Add this record to the list associated with this | |
150 | #message | |
151 | push @message_rec_list, $record; | |
152 | ||
153 | if ($messlen <= length($payload)) { | |
154 | #Shouldn't happen | |
155 | die "Internal error: invalid messlen: ".$messlen | |
156 | ." payload length:".length($payload)."\n"; | |
157 | } | |
158 | if (length($payload) + $record->decrypt_len >= $messlen) { | |
159 | #We can complete the message with this record | |
160 | $recoffset = $messlen - length($payload); | |
161 | $payload .= substr($record->decrypt_data, 0, $recoffset); | |
162 | push @message_frag_lens, $recoffset; | |
163 | $message = create_message($server, $mt, $payload, | |
164 | $startoffset); | |
165 | push @messages, $message; | |
166 | ||
167 | #Check if we have finished the handshake | |
168 | if ($mt == MT_FINISHED && $server) { | |
169 | $success = 1; | |
170 | $end = 1; | |
171 | } | |
172 | $payload = ""; | |
173 | } else { | |
174 | #This is just part of the total message | |
175 | $payload .= $record->decrypt_data; | |
176 | $recoffset = $record->decrypt_len; | |
177 | push @message_frag_lens, $record->decrypt_len; | |
178 | } | |
179 | print " Partial message data read: ".$recoffset." bytes\n"; | |
180 | } | |
181 | ||
182 | while ($record->decrypt_len > $recoffset) { | |
183 | #We are at the start of a new message | |
184 | if ($record->decrypt_len - $recoffset < 4) { | |
185 | #Whilst technically probably valid we can't cope with this | |
186 | die "End of record in the middle of a message header\n"; | |
187 | } | |
188 | @message_rec_list = ($record); | |
189 | my $lenhi; | |
190 | my $lenlo; | |
191 | ($mt, $lenhi, $lenlo) = unpack('CnC', | |
192 | substr($record->decrypt_data, | |
193 | $recoffset)); | |
194 | $messlen = ($lenhi << 8) | $lenlo; | |
195 | print " Message type: $message_type{$mt}\n"; | |
196 | print " Message Length: $messlen\n"; | |
197 | $startoffset = $recoffset; | |
198 | $recoffset += 4; | |
199 | $payload = ""; | |
200 | ||
201 | if ($recoffset < $record->decrypt_len) { | |
202 | #Some payload data is present in this record | |
203 | if ($record->decrypt_len - $recoffset >= $messlen) { | |
204 | #We can complete the message with this record | |
205 | $payload .= substr($record->decrypt_data, $recoffset, | |
206 | $messlen); | |
207 | $recoffset += $messlen; | |
208 | push @message_frag_lens, $messlen; | |
209 | $message = create_message($server, $mt, $payload, | |
210 | $startoffset); | |
211 | push @messages, $message; | |
212 | ||
213 | #Check if we have finished the handshake | |
214 | if ($mt == MT_FINISHED && $server) { | |
215 | $success = 1; | |
216 | $end = 1; | |
217 | } | |
218 | $payload = ""; | |
219 | } else { | |
220 | #This is just part of the total message | |
221 | $payload .= substr($record->decrypt_data, $recoffset, | |
222 | $record->decrypt_len - $recoffset); | |
223 | $recoffset = $record->decrypt_len; | |
224 | push @message_frag_lens, $recoffset; | |
225 | } | |
226 | } | |
227 | } | |
228 | } | |
229 | } elsif ($record->content_type == TLSProxy::Record::RT_APPLICATION_DATA) { | |
230 | print " [ENCRYPTED APPLICATION DATA]\n"; | |
231 | print " [".$record->decrypt_data."]\n"; | |
232 | } elsif ($record->content_type == TLSProxy::Record::RT_ALERT) { | |
233 | #For now assume all alerts are fatal | |
234 | $end = 1; | |
235 | } | |
236 | ||
237 | return @messages; | |
238 | } | |
239 | ||
240 | #Function to work out which sub-class we need to create and then | |
241 | #construct it | |
242 | sub create_message | |
243 | { | |
244 | my ($server, $mt, $data, $startoffset) = @_; | |
245 | my $message; | |
246 | ||
247 | #We only support ClientHello in this version...needs to be extended for | |
248 | #others | |
249 | if ($mt == MT_CLIENT_HELLO) { | |
250 | $message = TLSProxy::ClientHello->new( | |
251 | $server, | |
252 | $data, | |
253 | [@message_rec_list], | |
254 | $startoffset, | |
255 | [@message_frag_lens] | |
256 | ); | |
257 | $message->parse(); | |
a1accbb1 MC |
258 | } elsif ($mt == MT_SERVER_HELLO) { |
259 | $message = TLSProxy::ServerHello->new( | |
260 | $server, | |
261 | $data, | |
262 | [@message_rec_list], | |
263 | $startoffset, | |
264 | [@message_frag_lens] | |
265 | ); | |
266 | $message->parse(); | |
267 | } elsif ($mt == MT_SERVER_KEY_EXCHANGE) { | |
268 | $message = TLSProxy::ServerKeyExchange->new( | |
269 | $server, | |
270 | $data, | |
271 | [@message_rec_list], | |
272 | $startoffset, | |
273 | [@message_frag_lens] | |
274 | ); | |
275 | $message->parse(); | |
631c1206 MC |
276 | } else { |
277 | #Unknown message type | |
278 | $message = TLSProxy::Message->new( | |
279 | $server, | |
280 | $mt, | |
281 | $data, | |
282 | [@message_rec_list], | |
283 | $startoffset, | |
284 | [@message_frag_lens] | |
285 | ); | |
286 | } | |
287 | ||
288 | return $message; | |
289 | } | |
290 | ||
291 | sub end | |
292 | { | |
293 | my $class = shift; | |
294 | return $end; | |
295 | } | |
296 | sub success | |
297 | { | |
298 | my $class = shift; | |
299 | return $success; | |
300 | } | |
a1accbb1 MC |
301 | sub fail |
302 | { | |
303 | my $class = shift; | |
304 | return !$success && $end; | |
305 | } | |
631c1206 MC |
306 | sub new |
307 | { | |
308 | my $class = shift; | |
309 | my ($server, | |
310 | $mt, | |
311 | $data, | |
312 | $records, | |
313 | $startoffset, | |
314 | $message_frag_lens) = @_; | |
315 | ||
316 | my $self = { | |
317 | server => $server, | |
318 | data => $data, | |
319 | records => $records, | |
320 | mt => $mt, | |
321 | startoffset => $startoffset, | |
322 | message_frag_lens => $message_frag_lens | |
323 | }; | |
324 | ||
325 | return bless $self, $class; | |
326 | } | |
327 | ||
a1accbb1 MC |
328 | sub ciphersuite |
329 | { | |
330 | my $class = shift; | |
331 | if (@_) { | |
332 | $ciphersuite = shift; | |
333 | } | |
334 | return $ciphersuite; | |
335 | } | |
336 | ||
631c1206 MC |
337 | #Update all the underlying records with the modified data from this message |
338 | #Note: Does not currently support re-encrypting | |
339 | sub repack | |
340 | { | |
341 | my $self = shift; | |
342 | my $msgdata; | |
343 | ||
344 | my $numrecs = $#{$self->records}; | |
345 | ||
346 | $self->set_message_contents(); | |
347 | ||
348 | my $lenhi; | |
349 | my $lenlo; | |
350 | ||
351 | $lenlo = length($self->data) & 0xff; | |
352 | $lenhi = length($self->data) >> 8; | |
353 | my $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data; | |
354 | ||
355 | ||
356 | if ($numrecs == 0) { | |
357 | #The message is fully contained within one record | |
358 | my ($rec) = @{$self->records}; | |
359 | my $recdata = $rec->decrypt_data; | |
360 | ||
361 | if (length($msgdata) != ${$self->message_frag_lens}[0] | |
362 | + TLS_MESSAGE_HEADER_LENGTH) { | |
363 | #Message length has changed! Better adjust the record length | |
364 | my $diff = length($msgdata) - ${$self->message_frag_lens}[0] | |
365 | - TLS_MESSAGE_HEADER_LENGTH; | |
366 | $rec->len($rec->len + $diff); | |
367 | } | |
368 | ||
369 | $rec->data(substr($recdata, 0, $self->startoffset) | |
370 | .($msgdata) | |
371 | .substr($recdata, ${$self->message_frag_lens}[0] | |
372 | + TLS_MESSAGE_HEADER_LENGTH)); | |
373 | ||
374 | #Update the fragment len in case we changed it above | |
375 | ${$self->message_frag_lens}[0] = length($msgdata) | |
376 | - TLS_MESSAGE_HEADER_LENGTH; | |
377 | return; | |
378 | } | |
379 | ||
380 | #Note we don't currently support changing a fragmented message length | |
381 | my $recctr = 0; | |
382 | my $datadone = 0; | |
383 | foreach my $rec (@{$self->records}) { | |
384 | my $recdata = $rec->decrypt_data; | |
385 | if ($recctr == 0) { | |
386 | #This is the first record | |
387 | my $remainlen = length($recdata) - $self->startoffset; | |
388 | $rec->data(substr($recdata, 0, $self->startoffset) | |
389 | .substr(($msgdata), 0, $remainlen)); | |
390 | $datadone += $remainlen; | |
391 | } elsif ($recctr + 1 == $numrecs) { | |
392 | #This is the last record | |
393 | $rec->data(substr($msgdata, $datadone)); | |
394 | } else { | |
395 | #This is a middle record | |
396 | $rec->data(substr($msgdata, $datadone, length($rec->data))); | |
397 | $datadone += length($rec->data); | |
398 | } | |
399 | $recctr++; | |
400 | } | |
401 | } | |
402 | ||
403 | #To be overridden by sub-classes | |
404 | sub set_message_contents | |
405 | { | |
406 | } | |
407 | ||
408 | #Read only accessors | |
409 | sub server | |
410 | { | |
411 | my $self = shift; | |
412 | return $self->{server}; | |
413 | } | |
414 | ||
415 | #Read/write accessors | |
416 | sub mt | |
417 | { | |
418 | my $self = shift; | |
419 | if (@_) { | |
420 | $self->{mt} = shift; | |
421 | } | |
422 | return $self->{mt}; | |
423 | } | |
424 | sub data | |
425 | { | |
426 | my $self = shift; | |
427 | if (@_) { | |
428 | $self->{data} = shift; | |
429 | } | |
430 | return $self->{data}; | |
431 | } | |
432 | sub records | |
433 | { | |
434 | my $self = shift; | |
435 | if (@_) { | |
436 | $self->{records} = shift; | |
437 | } | |
438 | return $self->{records}; | |
439 | } | |
440 | sub startoffset | |
441 | { | |
442 | my $self = shift; | |
443 | if (@_) { | |
444 | $self->{startoffset} = shift; | |
445 | } | |
446 | return $self->{startoffset}; | |
447 | } | |
448 | sub message_frag_lens | |
449 | { | |
450 | my $self = shift; | |
451 | if (@_) { | |
452 | $self->{message_frag_lens} = shift; | |
453 | } | |
454 | return $self->{message_frag_lens}; | |
455 | } | |
456 | ||
457 | 1; |