From a1accbb1d704da9a25b18e7053ee191a8f510d93 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Fri, 7 Aug 2015 14:38:21 +0100 Subject: [PATCH] Extend TLSProxy capabilities Add ServerHello parsing to TLSProxy. Also add some (very) limited ServerKeyExchange parsing. Add the capability to set client and server cipher lists Fix a bug with fragment lengths Reviewed-by: Richard Levitte --- util/TLSProxy/Message.pm | 36 ++++- util/TLSProxy/Proxy.pm | 42 +++++- util/TLSProxy/ServerHello.pm | 235 +++++++++++++++++++++++++++++ util/TLSProxy/ServerKeyExchange.pm | 176 +++++++++++++++++++++ 4 files changed, 482 insertions(+), 7 deletions(-) create mode 100644 util/TLSProxy/ServerHello.pm create mode 100644 util/TLSProxy/ServerKeyExchange.pm diff --git a/util/TLSProxy/Message.pm b/util/TLSProxy/Message.pm index 811073bbfd..66a4a7bc12 100644 --- a/util/TLSProxy/Message.pm +++ b/util/TLSProxy/Message.pm @@ -98,6 +98,7 @@ my $success = 0; my $end = 0; my @message_rec_list = (); my @message_frag_lens = (); +my $ciphersuite = 0; sub clear { @@ -120,6 +121,8 @@ sub get_messages my @messages = (); my $message; + @message_frag_lens = (); + if ($serverin != $server && length($payload) != 0) { die "Changed peer, but we still have fragment data\n"; } @@ -252,6 +255,24 @@ sub create_message [@message_frag_lens] ); $message->parse(); + } elsif ($mt == MT_SERVER_HELLO) { + $message = TLSProxy::ServerHello->new( + $server, + $data, + [@message_rec_list], + $startoffset, + [@message_frag_lens] + ); + $message->parse(); + } elsif ($mt == MT_SERVER_KEY_EXCHANGE) { + $message = TLSProxy::ServerKeyExchange->new( + $server, + $data, + [@message_rec_list], + $startoffset, + [@message_frag_lens] + ); + $message->parse(); } else { #Unknown message type $message = TLSProxy::Message->new( @@ -277,7 +298,11 @@ sub success my $class = shift; return $success; } - +sub fail +{ + my $class = shift; + return !$success && $end; +} sub new { my $class = shift; @@ -300,6 +325,15 @@ sub new return bless $self, $class; } +sub ciphersuite +{ + my $class = shift; + if (@_) { + $ciphersuite = shift; + } + return $ciphersuite; +} + #Update all the underlying records with the modified data from this message #Note: Does not currently support re-encrypting sub repack diff --git a/util/TLSProxy/Proxy.pm b/util/TLSProxy/Proxy.pm index b4ee671c66..8c4f55db08 100644 --- a/util/TLSProxy/Proxy.pm +++ b/util/TLSProxy/Proxy.pm @@ -61,6 +61,8 @@ use IO::Select; use TLSProxy::Record; use TLSProxy::Message; use TLSProxy::ClientHello; +use TLSProxy::ServerHello; +use TLSProxy::ServerKeyExchange; sub new { @@ -82,6 +84,8 @@ sub new execute => $execute, cert => $cert, debug => $debug, + cipherc => "AES128-SHA", + ciphers => "", flight => 0, record_list => [], message_list => [], @@ -97,6 +101,8 @@ sub clear { my $self = shift; + $self->{cipherc} = "AES128-SHA"; + $self->{ciphers} = ""; $self->{flight} = 0; $self->{record_list} = []; $self->{message_list} = []; @@ -124,8 +130,13 @@ sub start open(STDOUT, ">", File::Spec->devnull()) or die "Failed to redirect stdout"; open(STDERR, ">&STDOUT"); - exec($self->execute." s_server -testmode -accept ".($self->server_port) - ." -cert ".$self->cert." -naccept 1"); + my $execcmd = $self->execute." s_server -testmode -accept " + .($self->server_port) + ." -cert ".$self->cert." -naccept 1"; + if ($self->ciphers ne "") { + $execcmd .= " -cipher ".$self->ciphers; + } + exec($execcmd); } my $oldstdout; @@ -155,9 +166,13 @@ sub start open(STDOUT, ">", File::Spec->devnull()) or die "Failed to redirect stdout"; open(STDERR, ">&STDOUT"); - exec($self->execute - ." s_client -cipher AES128-SHA -testmode -connect " - .($self->proxy_addr).":".($self->proxy_port)); + my $execcmd = $self->execute + ." s_client -testmode -connect " + .($self->proxy_addr).":".($self->proxy_port); + if ($self->cipherc ne "") { + $execcmd .= " -cipher ".$self->cipherc; + } + exec($execcmd); } } @@ -360,5 +375,20 @@ sub filter } return $self->{filter}; } - +sub cipherc +{ + my $self = shift; + if (@_) { + $self->{cipherc} = shift; + } + return $self->{cipherc}; +} +sub ciphers +{ + my $self = shift; + if (@_) { + $self->{ciphers} = shift; + } + return $self->{ciphers}; +} 1; diff --git a/util/TLSProxy/ServerHello.pm b/util/TLSProxy/ServerHello.pm new file mode 100644 index 0000000000..693430e9da --- /dev/null +++ b/util/TLSProxy/ServerHello.pm @@ -0,0 +1,235 @@ +# Written by Matt Caswell for the OpenSSL project. +# ==================================================================== +# Copyright (c) 1998-2015 The OpenSSL Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the OpenSSL Project +# for use in the OpenSSL Toolkit. (http://www.openssl.org/)" +# +# 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to +# endorse or promote products derived from this software without +# prior written permission. For written permission, please contact +# openssl-core@openssl.org. +# +# 5. Products derived from this software may not be called "OpenSSL" +# nor may "OpenSSL" appear in their names without prior written +# permission of the OpenSSL Project. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the OpenSSL Project +# for use in the OpenSSL Toolkit (http://www.openssl.org/)" +# +# THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ==================================================================== +# +# This product includes cryptographic software written by Eric Young +# (eay@cryptsoft.com). This product includes software written by Tim +# Hudson (tjh@cryptsoft.com). + +use strict; + +package TLSProxy::ServerHello; + +use parent 'TLSProxy::Message'; + +sub new +{ + my $class = shift; + my ($server, + $data, + $records, + $startoffset, + $message_frag_lens) = @_; + + my $self = $class->SUPER::new( + $server, + TLSProxy::Message::MT_SERVER_HELLO, + $data, + $records, + $startoffset, + $message_frag_lens); + + $self->{server_version} = 0; + $self->{random} = []; + $self->{session_id_len} = 0; + $self->{session} = ""; + $self->{ciphersuite} = 0; + $self->{comp_meth} = 0; + $self->{extensions_len} = 0; + $self->{extensions_data} = ""; + + return $self; +} + +sub parse +{ + my $self = shift; + my $ptr = 2; + my ($server_version) = unpack('n', $self->data); + my $random = substr($self->data, $ptr, 32); + $ptr += 32; + my $session_id_len = unpack('C', substr($self->data, $ptr)); + $ptr++; + my $session = substr($self->data, $ptr, $session_id_len); + $ptr += $session_id_len; + my $ciphersuite = unpack('n', substr($self->data, $ptr)); + $ptr += 2; + my $comp_meth = unpack('C', substr($self->data, $ptr)); + $ptr++; + my $extensions_len = unpack('n', substr($self->data, $ptr)); + $ptr += 2; + #For now we just deal with this as a block of data. In the future we will + #want to parse this + my $extension_data = substr($self->data, $ptr); + + if (length($extension_data) != $extensions_len) { + die "Invalid extension length\n"; + } + my %extensions = (); + while (length($extension_data) >= 4) { + my ($type, $size) = unpack("nn", $extension_data); + my $extdata = substr($extension_data, 4, $size); + $extension_data = substr($extension_data, 4 + $size); + $extensions{$type} = $extdata; + } + + $self->server_version($server_version); + $self->random($random); + $self->session_id_len($session_id_len); + $self->session($session); + $self->ciphersuite($ciphersuite); + $self->comp_meth($comp_meth); + $self->extensions_len($extensions_len); + $self->extension_data(\%extensions); + + $self->process_data(); + + print " Server Version:".$server_version."\n"; + print " Session ID Len:".$session_id_len."\n"; + print " Ciphersuite:".$ciphersuite."\n"; + print " Compression Method:".$comp_meth."\n"; + print " Extensions Len:".$extensions_len."\n"; +} + +#Perform any actions necessary based on the data we've seen +sub process_data +{ + my $self = shift; + + TLSProxy::Message->ciphersuite($self->ciphersuite); +} + +#Reconstruct the on-the-wire message data following changes +sub set_message_contents +{ + my $self = shift; + my $data; + + $data = pack('n', $self->server_version); + $data .= $self->random; + $data .= pack('C', $self->session_id_len); + $data .= $self->session; + $data .= pack('n', $self->ciphersuite); + $data .= pack('C', $self->comp_meth); + $data .= pack('n', $self->extensions_len); + foreach my $key (keys %{$self->extension_data}) { + my $extdata = ${$self->extension_data}{$key}; + $data .= pack("n", $key); + $data .= pack("n", length($extdata)); + $data .= $extdata; + } + + $self->data($data); +} + +#Read/write accessors +sub server_version +{ + my $self = shift; + if (@_) { + $self->{client_version} = shift; + } + return $self->{client_version}; +} +sub random +{ + my $self = shift; + if (@_) { + $self->{random} = shift; + } + return $self->{random}; +} +sub session_id_len +{ + my $self = shift; + if (@_) { + $self->{session_id_len} = shift; + } + return $self->{session_id_len}; +} +sub session +{ + my $self = shift; + if (@_) { + $self->{session} = shift; + } + return $self->{session}; +} +sub ciphersuite +{ + my $self = shift; + if (@_) { + $self->{ciphersuite} = shift; + } + return $self->{ciphersuite}; +} +sub comp_meth +{ + my $self = shift; + if (@_) { + $self->{comp_meth} = shift; + } + return $self->{comp_meth}; +} +sub extensions_len +{ + my $self = shift; + if (@_) { + $self->{extensions_len} = shift; + } + return $self->{extensions_len}; +} +sub extension_data +{ + my $self = shift; + if (@_) { + $self->{extension_data} = shift; + } + return $self->{extension_data}; +} +1; diff --git a/util/TLSProxy/ServerKeyExchange.pm b/util/TLSProxy/ServerKeyExchange.pm new file mode 100644 index 0000000000..3a91d1718c --- /dev/null +++ b/util/TLSProxy/ServerKeyExchange.pm @@ -0,0 +1,176 @@ +# Written by Matt Caswell for the OpenSSL project. +# ==================================================================== +# Copyright (c) 1998-2015 The OpenSSL Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the OpenSSL Project +# for use in the OpenSSL Toolkit. (http://www.openssl.org/)" +# +# 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to +# endorse or promote products derived from this software without +# prior written permission. For written permission, please contact +# openssl-core@openssl.org. +# +# 5. Products derived from this software may not be called "OpenSSL" +# nor may "OpenSSL" appear in their names without prior written +# permission of the OpenSSL Project. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the OpenSSL Project +# for use in the OpenSSL Toolkit (http://www.openssl.org/)" +# +# THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ==================================================================== +# +# This product includes cryptographic software written by Eric Young +# (eay@cryptsoft.com). This product includes software written by Tim +# Hudson (tjh@cryptsoft.com). + +use strict; + +package TLSProxy::ServerKeyExchange; + +use parent 'TLSProxy::Message'; + +sub new +{ + my $class = shift; + my ($server, + $data, + $records, + $startoffset, + $message_frag_lens) = @_; + + my $self = $class->SUPER::new( + $server, + TLSProxy::Message::MT_SERVER_KEY_EXCHANGE, + $data, + $records, + $startoffset, + $message_frag_lens); + + #DHE + $self->{p} = ""; + $self->{g} = ""; + $self->{pub_key} = ""; + $self->{sig} = ""; + + return $self; +} + +sub parse +{ + my $self = shift; + + #Minimal SKE parsing. Only supports DHE at the moment (if its not DHE + #the parsing data will be trash...which is ok as long as we don't try to + #use it) + + my $p_len = unpack('n', $self->data); + my $ptr = 2; + my $p = substr($self->data, $ptr, $p_len); + $ptr += $p_len; + + my $g_len = unpack('n', substr($self->data, $ptr)); + $ptr += 2; + my $g = substr($self->data, $ptr, $g_len); + $ptr += $g_len; + + my $pub_key_len = unpack('n', substr($self->data, $ptr)); + $ptr += 2; + my $pub_key = substr($self->data, $ptr, $pub_key_len); + $ptr += $g_len; + + #We assume its signed + my $sig_len = unpack('n', substr($self->data, $ptr)); + $ptr += 2; + my $sig = substr($self->data, $ptr, $sig_len); + $ptr += $sig_len; + + $self->p($p); + $self->g($g); + $self->pub_key($pub_key); + $self->sig($sig); +} + + +#Reconstruct the on-the-wire message data following changes +sub set_message_contents +{ + my $self = shift; + my $data; + + $data = pack('n', length($self->p)); + $data .= $self->p; + $data .= pack('n', length($self->g)); + $data .= $self->g; + $data .= pack('n', length($self->pub_key)); + $data .= $self->pub_key; + if (length($self->sig) > 0) { + $data .= pack('n', length($self->sig)); + $data .= $self->sig; + } + + $self->data($data); +} + +#Read/write accessors +#DHE +sub p +{ + my $self = shift; + if (@_) { + $self->{p} = shift; + } + return $self->{p}; +} +sub g +{ + my $self = shift; + if (@_) { + $self->{g} = shift; + } + return $self->{g}; +} +sub pub_key +{ + my $self = shift; + if (@_) { + $self->{pub_key} = shift; + } + return $self->{pub_key}; +} +sub sig +{ + my $self = shift; + if (@_) { + $self->{sig} = shift; + } + return $self->{sig}; +} +1; -- GitLab