diff --git a/scripts/base/protocols/ssl/main.bro b/scripts/base/protocols/ssl/main.bro index a1461db82d..d2b0332756 100644 --- a/scripts/base/protocols/ssl/main.bro +++ b/scripts/base/protocols/ssl/main.bro @@ -92,16 +92,20 @@ redef record Info += { delay_tokens: set[string] &optional; }; -const ports = { +const ssl_ports = { 443/tcp, 563/tcp, 585/tcp, 614/tcp, 636/tcp, 989/tcp, 990/tcp, 992/tcp, 993/tcp, 995/tcp, 5223/tcp }; -redef likely_server_ports += { ports }; + +const dtls_ports = { 4433/udp }; + +redef likely_server_ports += { ssl_ports, dtls_ports }; event bro_init() &priority=5 { Log::create_stream(SSL::LOG, [$columns=Info, $ev=log_ssl]); - Analyzer::register_for_ports(Analyzer::ANALYZER_SSL, ports); + Analyzer::register_for_ports(Analyzer::ANALYZER_SSL, ssl_ports); + Analyzer::register_for_ports(Analyzer::ANALYZER_DTLS, dtls_ports); } function set_session(c: connection) diff --git a/src/analyzer/protocol/ssl/CMakeLists.txt b/src/analyzer/protocol/ssl/CMakeLists.txt index 2591c5dfec..fab0d30f07 100644 --- a/src/analyzer/protocol/ssl/CMakeLists.txt +++ b/src/analyzer/protocol/ssl/CMakeLists.txt @@ -4,7 +4,13 @@ include(BroPlugin) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) bro_plugin_begin(Bro SSL) -bro_plugin_cc(SSL.cc Plugin.cc) +bro_plugin_cc(SSL.cc Plugin_SSL.cc) bro_plugin_bif(events.bif) -bro_plugin_pac(ssl.pac ssl-analyzer.pac ssl-protocol.pac ssl-defs.pac) +bro_plugin_pac(tls-handshake.pac tls-handshake-protocol.pac tls-handshake-analyzer.pac) +bro_plugin_pac(ssl.pac ssl-dtls-analyzer.pac ssl-analyzer.pac ssl-dtls-protocol.pac ssl-protocol.pac ssl-defs.pac) +bro_plugin_end() + +bro_plugin_begin(Bro DTLS) +bro_plugin_cc(DTLS.cc Plugin_DTLS.cc) +bro_plugin_pac(dtls.pac ssl-dtls-analyzer.pac dtls-analyzer.pac ssl-dtls-protocol.pac dtls-protocol.pac ssl-defs.pac) bro_plugin_end() diff --git a/src/analyzer/protocol/ssl/DTLS.cc b/src/analyzer/protocol/ssl/DTLS.cc new file mode 100644 index 0000000000..7c49dba439 --- /dev/null +++ b/src/analyzer/protocol/ssl/DTLS.cc @@ -0,0 +1,32 @@ + +#include "DTLS.h" +#include "Reporter.h" +#include "util.h" + +#include "events.bif.h" + +using namespace analyzer::dtls; + +DTLS_Analyzer::DTLS_Analyzer(Connection* c) +: analyzer::Analyzer("DTLS", c) + { + interp = new binpac::DTLS::SSL_Conn(this); + fprintf(stderr, "Instantiated :)\n"); + } + +DTLS_Analyzer::~DTLS_Analyzer() + { + delete interp; + } + +void DTLS_Analyzer::Done() + { + Analyzer::Done(); + } + +void DTLS_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint64 seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + fprintf(stderr, "Delivered packet :)\n"); + interp->NewData(orig, data, data + len); + } diff --git a/src/analyzer/protocol/ssl/DTLS.h b/src/analyzer/protocol/ssl/DTLS.h new file mode 100644 index 0000000000..c45a311c8c --- /dev/null +++ b/src/analyzer/protocol/ssl/DTLS.h @@ -0,0 +1,31 @@ +#ifndef ANALYZER_PROTOCOL_SSL_DTLS_H +#define ANALYZER_PROTOCOL_SSL_DTLS_H + +#include "events.bif.h" + +#include "analyzer/protocol/udp/UDP.h" +#include "dtls_pac.h" + +namespace analyzer { namespace dtls { + +class DTLS_Analyzer : public analyzer::Analyzer { +public: + DTLS_Analyzer(Connection* conn); + virtual ~DTLS_Analyzer(); + + // Overriden from Analyzer. + virtual void Done(); + virtual void DeliverPacket(int len, const u_char* data, bool orig, + uint64 seq, const IP_Hdr* ip, int caplen); + + + static analyzer::Analyzer* Instantiate(Connection* conn) + { return new DTLS_Analyzer(conn); } + +protected: + binpac::DTLS::SSL_Conn* interp; +}; + +} } // namespace analyzer::* + +#endif diff --git a/src/analyzer/protocol/ssl/Plugin_DTLS.cc b/src/analyzer/protocol/ssl/Plugin_DTLS.cc new file mode 100644 index 0000000000..6820816e31 --- /dev/null +++ b/src/analyzer/protocol/ssl/Plugin_DTLS.cc @@ -0,0 +1,26 @@ +// See the file in the main distribution directory for copyright. + + +#include "plugin/Plugin.h" + +#include "DTLS.h" + +namespace plugin { +namespace Bro_DTLS { + +class Plugin : public plugin::Plugin { +public: + plugin::Configuration Configure() + { + AddComponent(new ::analyzer::Component("DTLS", ::analyzer::dtls::DTLS_Analyzer::Instantiate)); + + plugin::Configuration config; + config.name = "Bro::DTLS"; + config.description = "DTLS analyzer"; + return config; + } +} plugin; + +} +} + diff --git a/src/analyzer/protocol/ssl/Plugin.cc b/src/analyzer/protocol/ssl/Plugin_SSL.cc similarity index 100% rename from src/analyzer/protocol/ssl/Plugin.cc rename to src/analyzer/protocol/ssl/Plugin_SSL.cc diff --git a/src/analyzer/protocol/ssl/SSL.cc b/src/analyzer/protocol/ssl/SSL.cc index 5e5d24888a..a26807c14b 100644 --- a/src/analyzer/protocol/ssl/SSL.cc +++ b/src/analyzer/protocol/ssl/SSL.cc @@ -5,6 +5,8 @@ #include "util.h" #include "events.bif.h" +#include "ssl_pac.h" +#include "tls-handshake_pac.h" using namespace analyzer::ssl; @@ -12,12 +14,14 @@ SSL_Analyzer::SSL_Analyzer(Connection* c) : tcp::TCP_ApplicationAnalyzer("SSL", c) { interp = new binpac::SSL::SSL_Conn(this); + handshake_interp = new binpac::TLSHandshake::Handshake_Conn(this); had_gap = false; } SSL_Analyzer::~SSL_Analyzer() { delete interp; + delete handshake_interp; } void SSL_Analyzer::Done() @@ -57,6 +61,21 @@ void SSL_Analyzer::DeliverStream(int len, const u_char* data, bool orig) } } +void SSL_Analyzer::SendHandshake(uint8 msg_type, uint32 length, const u_char* begin, const u_char* end, bool orig) + { + handshake_interp->set_msg_type(msg_type); + handshake_interp->set_msg_length(length); + try + { + handshake_interp->NewData(orig, begin, end); + } + catch ( const binpac::Exception& e ) + { + ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); + fprintf(stderr, "Handshake exception: %s\n", e.c_msg()); + } + } + void SSL_Analyzer::Undelivered(uint64 seq, int len, bool orig) { tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); diff --git a/src/analyzer/protocol/ssl/SSL.h b/src/analyzer/protocol/ssl/SSL.h index 5ef09aa147..a17611846c 100644 --- a/src/analyzer/protocol/ssl/SSL.h +++ b/src/analyzer/protocol/ssl/SSL.h @@ -4,7 +4,10 @@ #include "events.bif.h" #include "analyzer/protocol/tcp/TCP.h" -#include "ssl_pac.h" + +namespace binpac { namespace SSL { class SSL_Conn; } } + +namespace binpac { namespace TLSHandshake { class Handshake_Conn; } } namespace analyzer { namespace ssl { @@ -18,6 +21,8 @@ public: virtual void DeliverStream(int len, const u_char* data, bool orig); virtual void Undelivered(uint64 seq, int len, bool orig); + void SendHandshake(uint8 msg_type, uint32 length, const u_char* begin, const u_char* end, bool orig); + // Overriden from tcp::TCP_ApplicationAnalyzer. virtual void EndpointEOF(bool is_orig); @@ -26,6 +31,7 @@ public: protected: binpac::SSL::SSL_Conn* interp; + binpac::TLSHandshake::Handshake_Conn* handshake_interp; bool had_gap; }; diff --git a/src/analyzer/protocol/ssl/dtls-analyzer.pac b/src/analyzer/protocol/ssl/dtls-analyzer.pac new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/src/analyzer/protocol/ssl/dtls-analyzer.pac @@ -0,0 +1,2 @@ + + diff --git a/src/analyzer/protocol/ssl/dtls-protocol.pac b/src/analyzer/protocol/ssl/dtls-protocol.pac new file mode 100644 index 0000000000..94cddf9cbc --- /dev/null +++ b/src/analyzer/protocol/ssl/dtls-protocol.pac @@ -0,0 +1,40 @@ + +###################################################################### +# initial datatype for binpac +###################################################################### + +type DTLSPDU(is_orig: bool) = record { + records: SSLRecord(is_orig)[] &transient; +}; + +type SSLRecord(is_orig: bool) = record { + content_type: uint8; + version: uint16; + epoch: uint16; + sequence_number: uint48; + length: uint16; + rec: PlaintextRecord(this)[] &length=length; +# data: bytestring &restofdata &transient; +} &byteorder = bigendian, + &let { + parse : bool = $context.connection.proc_dtls(this, to_int()(sequence_number)); +}; + +type Handshake(rec: SSLRecord) = record { + msg_type: uint8; + length: uint24; + message_seq: uint16; + fragment_offset: uint24; + fragment_length: uint24; +} + +refine connection SSL_Conn += { + + function proc_dtls(pdu: SSLRecord, sequence: uint64): bool + %{ + fprintf(stderr, "Type: %d, sequence number: %d, epoch: %d\n", ${pdu.content_type}, sequence, ${pdu.epoch}); + + return true; + %} + +}; diff --git a/src/analyzer/protocol/ssl/dtls.pac b/src/analyzer/protocol/ssl/dtls.pac new file mode 100644 index 0000000000..50424f0d8c --- /dev/null +++ b/src/analyzer/protocol/ssl/dtls.pac @@ -0,0 +1,30 @@ +# binpac file for SSL analyzer + +%include binpac.pac +%include bro.pac + +%extern{ +#include "events.bif.h" +%} + +analyzer DTLS withcontext { + connection: SSL_Conn; + flow: DTLS_Flow; +}; + +connection SSL_Conn(bro_analyzer: BroAnalyzer) { + upflow = DTLS_Flow(true); + downflow = DTLS_Flow(false); +}; + +%include ssl-dtls-protocol.pac +%include dtls-protocol.pac + +flow DTLS_Flow(is_orig: bool) { +# flowunit = SSLRecord(is_orig) withcontext(connection, this); + datagram = DTLSPDU(is_orig) withcontext(connection, this); +} + +%include ssl-dtls-analyzer.pac +%include dtls-analyzer.pac +%include ssl-defs.pac diff --git a/src/analyzer/protocol/ssl/proc-certificate.pac b/src/analyzer/protocol/ssl/proc-certificate.pac new file mode 100644 index 0000000000..c2353e3a88 --- /dev/null +++ b/src/analyzer/protocol/ssl/proc-certificate.pac @@ -0,0 +1,30 @@ + function proc_certificate(is_orig: bool, certificates : bytestring[]) : bool + %{ + if ( certificates->size() == 0 ) + return true; + + ODesc common; + common.AddRaw("Analyzer::ANALYZER_SSL"); + common.Add(bro_analyzer()->Conn()->StartTime()); + common.AddRaw(is_orig ? "T" : "F", 1); + bro_analyzer()->Conn()->IDString(&common); + + for ( unsigned int i = 0; i < certificates->size(); ++i ) + { + const bytestring& cert = (*certificates)[i]; + + ODesc file_handle; + file_handle.Add(common.Description()); + file_handle.Add(i); + + string file_id = file_mgr->HashHandle(file_handle.Description()); + + file_mgr->DataIn(reinterpret_cast(cert.data()), + cert.length(), bro_analyzer()->GetAnalyzerTag(), + bro_analyzer()->Conn(), is_orig, file_id); + file_mgr->EndOfFile(file_id); + } + return true; + %} + + diff --git a/src/analyzer/protocol/ssl/proc-client-hello.pac b/src/analyzer/protocol/ssl/proc-client-hello.pac new file mode 100644 index 0000000000..601d0fce94 --- /dev/null +++ b/src/analyzer/protocol/ssl/proc-client-hello.pac @@ -0,0 +1,42 @@ + function proc_client_hello( + version : uint16, ts : double, + client_random : bytestring, + session_id : uint8[], + cipher_suites16 : uint16[], + cipher_suites24 : uint24[]) : bool + %{ + if ( ! version_ok(version) ) + { + bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version)); + bro_analyzer()->SetSkip(true); + } + else + bro_analyzer()->ProtocolConfirmation(); + + if ( ssl_client_hello ) + { + vector* cipher_suites = new vector(); + if ( cipher_suites16 ) + std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*cipher_suites)); + else + std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*cipher_suites), to_int()); + + VectorVal* cipher_vec = new VectorVal(internal_type("index_vec")->AsVectorType()); + for ( unsigned int i = 0; i < cipher_suites->size(); ++i ) + { + Val* ciph = new Val((*cipher_suites)[i], TYPE_COUNT); + cipher_vec->Assign(i, ciph); + } + + BifEvent::generate_ssl_client_hello(bro_analyzer(), bro_analyzer()->Conn(), + version, ts, new StringVal(client_random.length(), + (const char*) client_random.data()), + to_string_val(session_id), + cipher_vec); + + delete cipher_suites; + } + + return true; + %} + diff --git a/src/analyzer/protocol/ssl/proc-server-hello.pac b/src/analyzer/protocol/ssl/proc-server-hello.pac new file mode 100644 index 0000000000..2dfc940774 --- /dev/null +++ b/src/analyzer/protocol/ssl/proc-server-hello.pac @@ -0,0 +1,36 @@ + function proc_server_hello( + version : uint16, ts : double, + server_random : bytestring, + session_id : uint8[], + cipher_suites16 : uint16[], + cipher_suites24 : uint24[], + comp_method : uint8) : bool + %{ + if ( ! version_ok(version) ) + { + bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version)); + bro_analyzer()->SetSkip(true); + } + + if ( ssl_server_hello ) + { + vector* ciphers = new vector(); + + if ( cipher_suites16 ) + std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*ciphers)); + else + std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*ciphers), to_int()); + + BifEvent::generate_ssl_server_hello(bro_analyzer(), + bro_analyzer()->Conn(), + version, ts, new StringVal(server_random.length(), + (const char*) server_random.data()), + to_string_val(session_id), + ciphers->size()==0 ? 0 : ciphers->at(0), comp_method); + + delete ciphers; + } + + return true; + %} + diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index c835fd6632..709e8c32b2 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -1,352 +1,19 @@ # Analyzer for SSL (Bro-specific part). -%extern{ -#include -#include -#include -#include - -#include "util.h" - -#include "file_analysis/Manager.h" -%} - - -%header{ - class extract_certs { - public: - bytestring const& operator() (X509Certificate* cert) const - { - return cert->certificate(); - } - }; - - string orig_label(bool is_orig); - string handshake_type_label(int type); - %} - -%code{ -string orig_label(bool is_orig) - { - return string(is_orig ? "originator" :"responder"); - } - - string handshake_type_label(int type) - { - switch ( type ) { - case HELLO_REQUEST: return string("HELLO_REQUEST"); - case CLIENT_HELLO: return string("CLIENT_HELLO"); - case SERVER_HELLO: return string("SERVER_HELLO"); - case SESSION_TICKET: return string("SESSION_TICKET"); - case CERTIFICATE: return string("CERTIFICATE"); - case SERVER_KEY_EXCHANGE: return string("SERVER_KEY_EXCHANGE"); - case CERTIFICATE_REQUEST: return string("CERTIFICATE_REQUEST"); - case SERVER_HELLO_DONE: return string("SERVER_HELLO_DONE"); - case CERTIFICATE_VERIFY: return string("CERTIFICATE_VERIFY"); - case CLIENT_KEY_EXCHANGE: return string("CLIENT_KEY_EXCHANGE"); - case FINISHED: return string("FINISHED"); - case CERTIFICATE_URL: return string("CERTIFICATE_URL"); - case CERTIFICATE_STATUS: return string("CERTIFICATE_STATUS"); - default: return string(fmt("UNKNOWN (%d)", type)); - } - } - -%} - - -function to_string_val(data : uint8[]) : StringVal - %{ - char tmp[32]; - memset(tmp, 0, sizeof(tmp)); - - // Just return an empty string if the string is longer than 32 bytes - if ( data && data->size() <= 32 ) - { - for ( unsigned int i = data->size(); i > 0; --i ) - tmp[i-1] = (*data)[i-1]; - } - - return new StringVal(32, tmp); - %} - -function version_ok(vers : uint16) : bool - %{ - switch ( vers ) { - case SSLv20: - case SSLv30: - case TLSv10: - case TLSv11: - case TLSv12: - return true; - - default: - return false; - } - %} - refine connection SSL_Conn += { - %member{ - int established_; - %} + %include proc-client-hello.pac + %include proc-server-hello.pac + %include proc-certificate.pac - %init{ - established_ = false; - %} - - %cleanup{ - %} - - function proc_alert(rec: SSLRecord, level : int, desc : int) : bool - %{ - BifEvent::generate_ssl_alert(bro_analyzer(), bro_analyzer()->Conn(), - ${rec.is_orig}, level, desc); - return true; - %} - - function proc_client_hello(rec: SSLRecord, - version : uint16, ts : double, - client_random : bytestring, - session_id : uint8[], - cipher_suites16 : uint16[], - cipher_suites24 : uint24[]) : bool - %{ - if ( ! version_ok(version) ) - { - bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version)); - bro_analyzer()->SetSkip(true); - } - else - bro_analyzer()->ProtocolConfirmation(); - - if ( ssl_client_hello ) - { - vector* cipher_suites = new vector(); - if ( cipher_suites16 ) - std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*cipher_suites)); - else - std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*cipher_suites), to_int()); - - VectorVal* cipher_vec = new VectorVal(internal_type("index_vec")->AsVectorType()); - for ( unsigned int i = 0; i < cipher_suites->size(); ++i ) - { - Val* ciph = new Val((*cipher_suites)[i], TYPE_COUNT); - cipher_vec->Assign(i, ciph); - } - - BifEvent::generate_ssl_client_hello(bro_analyzer(), bro_analyzer()->Conn(), - version, ts, new StringVal(client_random.length(), - (const char*) client_random.data()), - to_string_val(session_id), - cipher_vec); - - delete cipher_suites; - } - - return true; - %} - - function proc_server_hello(rec: SSLRecord, - version : uint16, ts : double, - server_random : bytestring, - session_id : uint8[], - cipher_suites16 : uint16[], - cipher_suites24 : uint24[], - comp_method : uint8) : bool - %{ - if ( ! version_ok(version) ) - { - bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version)); - bro_analyzer()->SetSkip(true); - } - - if ( ssl_server_hello ) - { - vector* ciphers = new vector(); - - if ( cipher_suites16 ) - std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*ciphers)); - else - std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*ciphers), to_int()); - - BifEvent::generate_ssl_server_hello(bro_analyzer(), - bro_analyzer()->Conn(), - version, ts, new StringVal(server_random.length(), - (const char*) server_random.data()), - to_string_val(session_id), - ciphers->size()==0 ? 0 : ciphers->at(0), comp_method); - - delete ciphers; - } - - return true; - %} - - function proc_session_ticket_handshake(rec: SessionTicketHandshake, is_orig: bool): bool - %{ - if ( ssl_session_ticket_handshake ) - { - BifEvent::generate_ssl_session_ticket_handshake(bro_analyzer(), - bro_analyzer()->Conn(), - ${rec.ticket_lifetime_hint}, - new StringVal(${rec.data}.length(), (const char*) ${rec.data}.data())); - } - return true; - %} - - function proc_ssl_extension(rec: SSLRecord, type: int, sourcedata: const_bytestring) : bool - %{ - // We cheat a little bit here. We want to throw this event - // for every extension we encounter, even those that are - // handled by more specialized events later. To access the - // parsed data, we use sourcedata, which contains the whole - // data blob of the extension, including headers. We skip - // over those (4 bytes). - size_t length = sourcedata.length(); - if ( length < 4 ) - { - // This should be impossible due to the binpac parser - // and protocol description - bro_analyzer()->ProtocolViolation(fmt("Impossible extension length: %zu", length)); - bro_analyzer()->SetSkip(true); - return true; - } - - length -= 4; - const unsigned char* data = sourcedata.begin() + 4; - - if ( ssl_extension ) - BifEvent::generate_ssl_extension(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, type, - new StringVal(length, reinterpret_cast(data))); - return true; - %} - - function proc_ec_point_formats(rec: SSLRecord, point_format_list: uint8[]) : bool - %{ - VectorVal* points = new VectorVal(internal_type("index_vec")->AsVectorType()); - - if ( point_format_list ) - { - for ( unsigned int i = 0; i < point_format_list->size(); ++i ) - points->Assign(i, new Val((*point_format_list)[i], TYPE_COUNT)); - } - - BifEvent::generate_ssl_extension_ec_point_formats(bro_analyzer(), bro_analyzer()->Conn(), - ${rec.is_orig}, points); - - return true; - %} - - function proc_elliptic_curves(rec: SSLRecord, list: uint16[]) : bool - %{ - VectorVal* curves = new VectorVal(internal_type("index_vec")->AsVectorType()); - - if ( list ) - { - for ( unsigned int i = 0; i < list->size(); ++i ) - curves->Assign(i, new Val((*list)[i], TYPE_COUNT)); - } - - BifEvent::generate_ssl_extension_elliptic_curves(bro_analyzer(), bro_analyzer()->Conn(), - ${rec.is_orig}, curves); - - return true; - %} - - function proc_apnl(rec: SSLRecord, protocols: ProtocolName[]) : bool - %{ - VectorVal* plist = new VectorVal(internal_type("string_vec")->AsVectorType()); - - if ( protocols ) - { - for ( unsigned int i = 0; i < protocols->size(); ++i ) - plist->Assign(i, new StringVal((*protocols)[i]->name().length(), (const char*) (*protocols)[i]->name().data())); - } - - BifEvent::generate_ssl_extension_application_layer_protocol_negotiation(bro_analyzer(), bro_analyzer()->Conn(), - ${rec.is_orig}, plist); - - return true; - %} - - function proc_server_name(rec: SSLRecord, list: ServerName[]) : bool - %{ - VectorVal* servers = new VectorVal(internal_type("string_vec")->AsVectorType()); - - if ( list ) - { - for ( unsigned int i = 0, j = 0; i < list->size(); ++i ) - { - ServerName* servername = (*list)[i]; - if ( servername->name_type() != 0 ) - { - bro_analyzer()->Weird(fmt("Encountered unknown type in server name ssl extension: %d", servername->name_type())); - continue; - } - - if ( servername->host_name() ) - servers->Assign(j++, new StringVal(servername->host_name()->host_name().length(), (const char*) servername->host_name()->host_name().data())); - else - bro_analyzer()->Weird("Empty server_name extension in ssl connection"); - } - } - - BifEvent::generate_ssl_extension_server_name(bro_analyzer(), bro_analyzer()->Conn(), - ${rec.is_orig}, servers); - - return true; - %} - - function proc_certificate(rec: SSLRecord, certificates : bytestring[]) : bool - %{ - if ( certificates->size() == 0 ) - return true; - - ODesc common; - common.AddRaw("Analyzer::ANALYZER_SSL"); - common.Add(bro_analyzer()->Conn()->StartTime()); - common.AddRaw(${rec.is_orig} ? "T" : "F", 1); - bro_analyzer()->Conn()->IDString(&common); - - for ( unsigned int i = 0; i < certificates->size(); ++i ) - { - const bytestring& cert = (*certificates)[i]; - - ODesc file_handle; - file_handle.Add(common.Description()); - file_handle.Add(i); - - string file_id = file_mgr->HashHandle(file_handle.Description()); - - file_mgr->DataIn(reinterpret_cast(cert.data()), - cert.length(), bro_analyzer()->GetAnalyzerTag(), - bro_analyzer()->Conn(), ${rec.is_orig}, file_id); - file_mgr->EndOfFile(file_id); - } - return true; - %} - - function proc_v2_certificate(rec: SSLRecord, cert : bytestring) : bool + function proc_v2_certificate(is_orig: bool, cert : bytestring) : bool %{ vector* cert_list = new vector(1,cert); - bool ret = proc_certificate(rec, cert_list); + bool ret = proc_certificate(is_orig, cert_list); delete cert_list; return ret; %} - function proc_v3_certificate(rec: SSLRecord, cl : X509Certificate[]) : bool - %{ - vector* certs = cl; - vector* cert_list = new vector(); - - std::transform(certs->begin(), certs->end(), - std::back_inserter(*cert_list), extract_certs()); - - bool ret = proc_certificate(rec, cert_list); - delete cert_list; - return ret; - %} function proc_v2_client_master_key(rec: SSLRecord, cipher_kind: int) : bool %{ @@ -356,209 +23,40 @@ refine connection SSL_Conn += { return true; %} - function proc_unknown_handshake(hs: Handshake, is_orig: bool) : bool + function proc_handshake(rec: SSLRecord, msg_type: uint8, length: uint24, data: bytestring, is_orig: bool) : bool %{ - bro_analyzer()->ProtocolViolation(fmt("unknown handshake message (%d) from %s", - ${hs.msg_type}, orig_label(is_orig).c_str())); + fprintf(stderr, "Forwarding to Handshake analyzer: msg_type: %u, length: %u\n", msg_type, to_int()(length)); + fprintf(stderr, "%u\n", data.end() - data.begin()); + bro_analyzer()->SendHandshake(msg_type, to_int()(length), data.begin(), data.end(), is_orig); return true; %} - - function proc_unknown_record(rec: SSLRecord) : bool - %{ - bro_analyzer()->ProtocolViolation(fmt("unknown SSL record type (%d) from %s", - ${rec.content_type}, - orig_label(${rec.is_orig}).c_str())); - return true; - %} - - function proc_ciphertext_record(rec : SSLRecord) : bool - %{ - if ( client_state_ == STATE_ENCRYPTED && - server_state_ == STATE_ENCRYPTED && - established_ == false ) - { - established_ = true; - BifEvent::generate_ssl_established(bro_analyzer(), - bro_analyzer()->Conn()); - } - - BifEvent::generate_ssl_encrypted_data(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.content_type}, ${rec.length}); - - return true; - %} - - function proc_heartbeat(rec : SSLRecord, type: uint8, payload_length: uint16, data: bytestring) : bool - %{ - BifEvent::generate_ssl_heartbeat(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}, type, payload_length, - new StringVal(data.length(), (const char*) data.data())); - return true; - %} - - function proc_check_v2_server_hello_version(version: uint16) : bool - %{ - if ( version != SSLv20 ) - { - bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL server hello. Version: %d", version)); - bro_analyzer()->SetSkip(true); - return false; - } - - return true; - %} - - function proc_certificate_status(rec : SSLRecord, status_type: uint8, response: bytestring) : bool - %{ - if ( status_type == 1 ) // ocsp - { - BifEvent::generate_ssl_stapled_ocsp(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, - new StringVal(response.length(), - (const char*) response.data())); - } - - return true; - %} - - function proc_ec_server_key_exchange(rec: SSLRecord, curve_type: uint8, curve: uint16) : bool - %{ - if ( curve_type == NAMED_CURVE ) - BifEvent::generate_ssl_server_curve(bro_analyzer(), - bro_analyzer()->Conn(), curve); - - return true; - %} - - function proc_dh_server_key_exchange(rec: SSLRecord, p: bytestring, g: bytestring, Ys: bytestring) : bool - %{ - BifEvent::generate_ssl_dh_server_params(bro_analyzer(), - bro_analyzer()->Conn(), - new StringVal(p.length(), (const char*) p.data()), - new StringVal(g.length(), (const char*) g.data()), - new StringVal(Ys.length(), (const char*) Ys.data()) - ); - - return true; - %} - - function proc_ccs(rec: SSLRecord) : bool - %{ - BifEvent::generate_ssl_change_cipher_spec(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}); - - return true; - %} - - function proc_handshake(rec: SSLRecord, msg_type: uint8, length: uint24) : bool - %{ - BifEvent::generate_ssl_handshake_message(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, msg_type, to_int()(length)); - - return true; - %} - }; -refine typeattr Alert += &let { - proc : bool = $context.connection.proc_alert(rec, level, description); -}; refine typeattr V2Error += &let { proc : bool = $context.connection.proc_alert(rec, -1, error_code); }; -refine typeattr Heartbeat += &let { - proc : bool = $context.connection.proc_heartbeat(rec, type, payload_length, data); -}; - -refine typeattr ClientHello += &let { - proc : bool = $context.connection.proc_client_hello(rec, client_version, - gmt_unix_time, random_bytes, - session_id, csuits, 0); -}; refine typeattr V2ClientHello += &let { - proc : bool = $context.connection.proc_client_hello(rec, client_version, 0, + proc : bool = $context.connection.proc_client_hello(client_version, 0, challenge, session_id, 0, ciphers); }; -refine typeattr ServerHello += &let { - proc : bool = $context.connection.proc_server_hello(rec, server_version, - gmt_unix_time, random_bytes, session_id, cipher_suite, 0, - compression_method); -}; - refine typeattr V2ServerHello += &let { check_v2 : bool = $context.connection.proc_check_v2_server_hello_version(server_version); - proc : bool = $context.connection.proc_server_hello(rec, server_version, 0, + proc : bool = $context.connection.proc_server_hello(server_version, 0, conn_id_data, 0, 0, ciphers, 0) &requires(check_v2) &if(check_v2 == true); - cert : bool = $context.connection.proc_v2_certificate(rec, cert_data) + cert : bool = $context.connection.proc_v2_certificate(rec.is_orig, cert_data) &requires(proc) &requires(check_v2) &if(check_v2 == true); }; -refine typeattr Certificate += &let { - proc : bool = $context.connection.proc_v3_certificate(rec, certificates); -}; - refine typeattr V2ClientMasterKey += &let { proc : bool = $context.connection.proc_v2_client_master_key(rec, cipher_kind); }; -refine typeattr UnknownHandshake += &let { - proc : bool = $context.connection.proc_unknown_handshake(hs, is_orig); -}; - -refine typeattr SessionTicketHandshake += &let { - proc : bool = $context.connection.proc_session_ticket_handshake(this, rec.is_orig); -} - -refine typeattr UnknownRecord += &let { - proc : bool = $context.connection.proc_unknown_record(rec); -}; - -refine typeattr CiphertextRecord += &let { - proc : bool = $context.connection.proc_ciphertext_record(rec); -} - -refine typeattr SSLExtension += &let { - proc : bool = $context.connection.proc_ssl_extension(rec, type, sourcedata); -}; - -refine typeattr EcPointFormats += &let { - proc : bool = $context.connection.proc_ec_point_formats(rec, point_format_list); -}; - -refine typeattr EllipticCurves += &let { - proc : bool = $context.connection.proc_elliptic_curves(rec, elliptic_curve_list); -}; - -refine typeattr ApplicationLayerProtocolNegotiationExtension += &let { - proc : bool = $context.connection.proc_apnl(rec, protocol_name_list); -}; - -refine typeattr ServerNameExt += &let { - proc : bool = $context.connection.proc_server_name(rec, server_names); -}; - -refine typeattr CertificateStatus += &let { - proc : bool = $context.connection.proc_certificate_status(rec, status_type, response); -}; - -refine typeattr EcServerKeyExchange += &let { - proc : bool = $context.connection.proc_ec_server_key_exchange(rec, curve_type, curve); -}; - -refine typeattr DhServerKeyExchange += &let { - proc : bool = $context.connection.proc_dh_server_key_exchange(rec, dh_p, dh_g, dh_Ys); -}; - -refine typeattr ChangeCipherSpec += &let { - proc : bool = $context.connection.proc_ccs(rec); -}; - refine typeattr Handshake += &let { - proc : bool = $context.connection.proc_handshake(rec, msg_type, length); + proc : bool = $context.connection.proc_handshake(rec, msg_type, length, data, rec.is_orig); }; diff --git a/src/analyzer/protocol/ssl/ssl-defs.pac b/src/analyzer/protocol/ssl/ssl-defs.pac index 29eb1d1fb9..c29bbcbabe 100644 --- a/src/analyzer/protocol/ssl/ssl-defs.pac +++ b/src/analyzer/protocol/ssl/ssl-defs.pac @@ -1,5 +1,84 @@ # Some common definitions for the SSL and SSL record-layer analyzers. +type uint24 = record { + byte1 : uint8; + byte2 : uint8; + byte3 : uint8; +}; + +type uint48 = record { + byte1 : uint8; + byte2 : uint8; + byte3 : uint8; + byte4 : uint8; + byte5 : uint8; + byte6 : uint8; +}; + + +%header{ + string orig_label(bool is_orig); + %} + + +%code{ +string orig_label(bool is_orig) + { + return string(is_orig ? "originator" :"responder"); + } +%} + +%header{ + class to_int { + public: + int operator()(uint24 * num) const + { + return (num->byte1() << 16) | (num->byte2() << 8) | num->byte3(); + } + + uint64 operator()(uint48 * num) const + { + return ((uint64)num->byte1() << 40) | ((uint64)num->byte2() << 32) | ((uint64)num->byte3() << 24) | + ((uint64)num->byte4() << 16) | ((uint64)num->byte5() << 8) | (uint64)num->byte6(); + } + }; + + string state_label(int state_nr); +%} + +extern type to_int; + +function to_string_val(data : uint8[]) : StringVal + %{ + char tmp[32]; + memset(tmp, 0, sizeof(tmp)); + + // Just return an empty string if the string is longer than 32 bytes + if ( data && data->size() <= 32 ) + { + for ( unsigned int i = data->size(); i > 0; --i ) + tmp[i-1] = (*data)[i-1]; + } + + return new StringVal(32, tmp); + %} + +function version_ok(vers : uint16) : bool + %{ + switch ( vers ) { + case SSLv20: + case SSLv30: + case TLSv10: + case TLSv11: + case TLSv12: + return true; + + default: + return false; + } + %} + + %extern{ #include using std::string; diff --git a/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac b/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac new file mode 100644 index 0000000000..0e8418644e --- /dev/null +++ b/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac @@ -0,0 +1,106 @@ + +%extern{ +#include +#include +#include +#include + +#include "util.h" + +#include "file_analysis/Manager.h" +%} + +refine connection SSL_Conn += { + + %member{ + int established_; + %} + + %init{ + established_ = false; + %} + + %cleanup{ + %} + + function proc_alert(rec: SSLRecord, level : int, desc : int) : bool + %{ + BifEvent::generate_ssl_alert(bro_analyzer(), bro_analyzer()->Conn(), + ${rec.is_orig}, level, desc); + return true; + %} + function proc_unknown_record(rec: SSLRecord) : bool + %{ + bro_analyzer()->ProtocolViolation(fmt("unknown SSL record type (%d) from %s", + ${rec.content_type}, + orig_label(${rec.is_orig}).c_str())); + return true; + %} + + function proc_ciphertext_record(rec : SSLRecord) : bool + %{ + if ( client_state_ == STATE_ENCRYPTED && + server_state_ == STATE_ENCRYPTED && + established_ == false ) + { + established_ = true; + BifEvent::generate_ssl_established(bro_analyzer(), + bro_analyzer()->Conn()); + } + + BifEvent::generate_ssl_encrypted_data(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.content_type}, ${rec.length}); + + return true; + %} + + function proc_heartbeat(rec : SSLRecord, type: uint8, payload_length: uint16, data: bytestring) : bool + %{ + BifEvent::generate_ssl_heartbeat(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}, type, payload_length, + new StringVal(data.length(), (const char*) data.data())); + return true; + %} + + function proc_check_v2_server_hello_version(version: uint16) : bool + %{ + if ( version != SSLv20 ) + { + bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL server hello. Version: %d", version)); + bro_analyzer()->SetSkip(true); + return false; + } + + return true; + %} + + + function proc_ccs(rec: SSLRecord) : bool + %{ + BifEvent::generate_ssl_change_cipher_spec(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.is_orig}); + + return true; + %} + +}; + +refine typeattr Alert += &let { + proc : bool = $context.connection.proc_alert(rec, level, description); +}; + +refine typeattr Heartbeat += &let { + proc : bool = $context.connection.proc_heartbeat(rec, type, payload_length, data); +}; + +refine typeattr UnknownRecord += &let { + proc : bool = $context.connection.proc_unknown_record(rec); +}; + +refine typeattr CiphertextRecord += &let { + proc : bool = $context.connection.proc_ciphertext_record(rec); +} + +refine typeattr ChangeCipherSpec += &let { + proc : bool = $context.connection.proc_ccs(rec); +}; diff --git a/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac b/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac new file mode 100644 index 0000000000..c3277d150e --- /dev/null +++ b/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac @@ -0,0 +1,134 @@ + +###################################################################### +# General definitions +###################################################################### + +type PlaintextRecord(rec: SSLRecord) = case rec.content_type of { + CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec); + ALERT -> alert : Alert(rec); + HEARTBEAT -> heartbeat: Heartbeat(rec); + APPLICATION_DATA -> app_data : ApplicationData(rec); + default -> unknown_record : UnknownRecord(rec); +}; + + +###################################################################### +# Encryption Tracking +###################################################################### + +enum AnalyzerState { + STATE_CLEAR, + STATE_ENCRYPTED +}; + +%code{ + string state_label(int state_nr) + { + switch ( state_nr ) { + case STATE_CLEAR: + return string("CLEAR"); + + case STATE_ENCRYPTED: + return string("ENCRYPTED"); + + default: + return string(fmt("UNKNOWN (%d)", state_nr)); + } + } +%} + +###################################################################### +# Change Cipher Spec Protocol (7.1.) +###################################################################### + +type ChangeCipherSpec(rec: SSLRecord) = record { + type : uint8; +} &length = 1, &let { + state_changed : bool = + $context.connection.startEncryption(rec.is_orig); +}; + + +###################################################################### +# Alert Protocol (7.2.) +###################################################################### + +type Alert(rec: SSLRecord) = record { + level : uint8; + description: uint8; +}; + + +###################################################################### +# V3 Application Data +###################################################################### + +# Application data should always be encrypted, so we should not +# reach this point. +type ApplicationData(rec: SSLRecord) = record { + data : bytestring &restofdata &transient; +}; + +###################################################################### +# V3 Heartbeat +###################################################################### + +type Heartbeat(rec: SSLRecord) = record { + type : uint8; + payload_length : uint16; + data : bytestring &restofdata; +}; + + + +###################################################################### +# Fragmentation (6.2.1.) +###################################################################### + +type UnknownRecord(rec: SSLRecord) = record { + cont : bytestring &restofdata &transient; +}; + +type CiphertextRecord(rec: SSLRecord) = record { + cont : bytestring &restofdata &transient; +}; + +###################################################################### +# binpac analyzer for SSL including +###################################################################### + +refine connection SSL_Conn += { + + %member{ + int client_state_; + int server_state_; + int record_layer_version_; + %} + + %init{ + server_state_ = STATE_CLEAR; + client_state_ = STATE_CLEAR; + record_layer_version_ = UNKNOWN_VERSION; + %} + + function client_state() : int %{ return client_state_; %} + + function server_state() : int %{ return client_state_; %} + + function state(is_orig: bool) : int + %{ + if ( is_orig ) + return client_state_; + else + return server_state_; + %} + + function startEncryption(is_orig: bool) : bool + %{ + if ( is_orig ) + client_state_ = STATE_ENCRYPTED; + else + server_state_ = STATE_ENCRYPTED; + return true; + %} +}; diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index cb794bd8a4..d5628e9cc7 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -2,30 +2,6 @@ # To be used in conjunction with an SSL record-layer analyzer. # Separation is necessary due to possible fragmentation of SSL records. -###################################################################### -# General definitions -###################################################################### - -type uint24 = record { - byte1 : uint8; - byte2 : uint8; - byte3 : uint8; -}; - -%header{ - class to_int { - public: - int operator()(uint24 * num) const - { - return (num->byte1() << 16) | (num->byte2() << 8) | num->byte3(); - } - }; - - string state_label(int state_nr); -%} - -extern type to_int; - type SSLRecord(is_orig: bool) = record { head0 : uint8; head1 : uint8; @@ -58,161 +34,20 @@ type RecordText(rec: SSLRecord) = case $context.connection.state(rec.is_orig) of -> plaintext : PlaintextRecord(rec); }; -type PlaintextRecord(rec: SSLRecord) = case rec.content_type of { - CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec); - ALERT -> alert : Alert(rec); +refine casetype PlaintextRecord += { HANDSHAKE -> handshake : Handshake(rec); - HEARTBEAT -> heartbeat: Heartbeat(rec); - APPLICATION_DATA -> app_data : ApplicationData(rec); V2_ERROR -> v2_error : V2Error(rec); V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec); V2_CLIENT_MASTER_KEY -> v2_client_master_key : V2ClientMasterKey(rec); V2_SERVER_HELLO -> v2_server_hello : V2ServerHello(rec); - default -> unknown_record : UnknownRecord(rec); }; -###################################################################### -# TLS Extensions -###################################################################### - -type SSLExtension(rec: SSLRecord) = record { - type: uint16; - data_len: uint16; - - # Pretty code ahead. Deal with the fact that perhaps extensions are - # not really present and we do not want to fail because of that. - ext: case type of { - EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION -> apnl: ApplicationLayerProtocolNegotiationExtension(rec)[] &until($element == 0 || $element != 0); - EXT_ELLIPTIC_CURVES -> elliptic_curves: EllipticCurves(rec)[] &until($element == 0 || $element != 0); - EXT_EC_POINT_FORMATS -> ec_point_formats: EcPointFormats(rec)[] &until($element == 0 || $element != 0); -# EXT_STATUS_REQUEST -> status_request: StatusRequest(rec)[] &until($element == 0 || $element != 0); - EXT_SERVER_NAME -> server_name: ServerNameExt(rec)[] &until($element == 0 || $element != 0); - default -> data: bytestring &restofdata; - }; -} &length=data_len+4 &exportsourcedata; - -type ServerNameHostName() = record { - length: uint16; - host_name: bytestring &length=length; +type Handshake(rec: SSLRecord) = record { + msg_type: uint8; + length: uint24; + data: bytestring &length=to_int()(length); }; -type ServerName() = record { - name_type: uint8; # has to be 0 for host-name - name: case name_type of { - 0 -> host_name: ServerNameHostName; - default -> data : bytestring &restofdata &transient; # unknown name - }; -}; - -type ServerNameExt(rec: SSLRecord) = record { - length: uint16; - server_names: ServerName[] &until($input.length() == 0); -} &length=length+2; - -# Do not parse for now. Structure is correct, but only contains asn.1 data that we would not use further. -#type OcspStatusRequest(rec: SSLRecord) = record { -# responder_id_list_length: uint16; -# responder_id_list: bytestring &length=responder_id_list_length; -# request_extensions_length: uint16; -# request_extensions: bytestring &length=request_extensions_length; -#}; -# -#type StatusRequest(rec: SSLRecord) = record { -# status_type: uint8; # 1 -> ocsp -# req: case status_type of { -# 1 -> ocsp_status_request: OcspStatusRequest(rec); -# default -> data : bytestring &restofdata &transient; # unknown -# }; -#}; - -type EcPointFormats(rec: SSLRecord) = record { - length: uint8; - point_format_list: uint8[length]; -}; - -type EllipticCurves(rec: SSLRecord) = record { - length: uint16; - elliptic_curve_list: uint16[length/2]; -}; - -type ProtocolName() = record { - length: uint8; - name: bytestring &length=length; -}; - -type ApplicationLayerProtocolNegotiationExtension(rec: SSLRecord) = record { - length: uint16; - protocol_name_list: ProtocolName[] &until($input.length() == 0); -} &length=length+2; - -###################################################################### -# Encryption Tracking -###################################################################### - -enum AnalyzerState { - STATE_CLEAR, - STATE_ENCRYPTED -}; - -%code{ - string state_label(int state_nr) - { - switch ( state_nr ) { - case STATE_CLEAR: - return string("CLEAR"); - - case STATE_ENCRYPTED: - return string("ENCRYPTED"); - - default: - return string(fmt("UNKNOWN (%d)", state_nr)); - } - } -%} - -###################################################################### -# SSLv3 Handshake Protocols (7.) -###################################################################### - -enum HandshakeType { - HELLO_REQUEST = 0, - CLIENT_HELLO = 1, - SERVER_HELLO = 2, - SESSION_TICKET = 4, # RFC 5077 - CERTIFICATE = 11, - SERVER_KEY_EXCHANGE = 12, - CERTIFICATE_REQUEST = 13, - SERVER_HELLO_DONE = 14, - CERTIFICATE_VERIFY = 15, - CLIENT_KEY_EXCHANGE = 16, - FINISHED = 20, - CERTIFICATE_URL = 21, # RFC 3546 - CERTIFICATE_STATUS = 22, # RFC 3546 -}; - - -###################################################################### -# V3 Change Cipher Spec Protocol (7.1.) -###################################################################### - -type ChangeCipherSpec(rec: SSLRecord) = record { - type : uint8; -} &length = 1, &let { - state_changed : bool = - $context.connection.startEncryption(rec.is_orig); -}; - - -###################################################################### -# V3 Alert Protocol (7.2.) -###################################################################### - -type Alert(rec: SSLRecord) = record { - level : uint8; - description: uint8; -}; - - ###################################################################### # V2 Error Records (SSLv2 2.7.) ###################################################################### @@ -224,53 +59,6 @@ type V2Error(rec: SSLRecord) = record { }; -###################################################################### -# V3 Application Data -###################################################################### - -# Application data should always be encrypted, so we should not -# reach this point. -type ApplicationData(rec: SSLRecord) = record { - data : bytestring &restofdata &transient; -}; - -###################################################################### -# V3 Heartbeat -###################################################################### - -type Heartbeat(rec: SSLRecord) = record { - type : uint8; - payload_length : uint16; - data : bytestring &restofdata; -}; - -###################################################################### -# V3 Hello Request (7.4.1.1.) -###################################################################### - -# Hello Request is empty -type HelloRequest(rec: SSLRecord) = empty; - - -###################################################################### -# V3 Client Hello (7.4.1.2.) -###################################################################### - -type ClientHello(rec: SSLRecord) = record { - client_version : uint16; - gmt_unix_time : uint32; - random_bytes : bytestring &length = 28; - session_len : uint8; - session_id : uint8[session_len]; - csuit_len : uint16 &check(csuit_len > 1 && csuit_len % 2 == 0); - csuits : uint16[csuit_len/2]; - cmeth_len : uint8 &check(cmeth_len > 0); - cmeths : uint8[cmeth_len]; - # This weirdness is to deal with the possible existence or absence - # of the following fields. - ext_len: uint16[] &until($element == 0 || $element != 0); - extensions : SSLExtension(rec)[] &until($input.length() == 0); -}; ###################################################################### # V2 Client Hello (SSLv2 2.5.) @@ -288,26 +76,6 @@ type V2ClientHello(rec: SSLRecord) = record { }; -###################################################################### -# V3 Server Hello (7.4.1.3.) -###################################################################### - -type ServerHello(rec: SSLRecord) = record { - server_version : uint16; - gmt_unix_time : uint32; - random_bytes : bytestring &length = 28; - session_len : uint8; - session_id : uint8[session_len]; - cipher_suite : uint16[1]; - compression_method : uint8; - # This weirdness is to deal with the possible existence or absence - # of the following fields. - ext_len: uint16[] &until($element == 0 || $element != 0); - extensions : SSLExtension(rec)[] &until($input.length() == 0); -} &let { - cipher_set : bool = - $context.connection.set_cipher(cipher_suite[0]); -}; ###################################################################### # V2 Server Hello (SSLv2 2.6.) @@ -329,298 +97,6 @@ type V2ServerHello(rec: SSLRecord) = record { }; -###################################################################### -# V3 Server Certificate (7.4.2.) -###################################################################### - -type X509Certificate = record { - length : uint24; - certificate : bytestring &length = to_int()(length); -}; - -type Certificate(rec: SSLRecord) = record { - length : uint24; - certificates : X509Certificate[] &until($input.length() == 0); -} &length = to_int()(length)+3; - -# OCSP Stapling - -type CertificateStatus(rec: SSLRecord) = record { - status_type: uint8; # 1 = ocsp, everything else is undefined - length : uint24; - response: bytestring &restofdata; -}; - -###################################################################### -# V3 Server Key Exchange Message (7.4.3.) -###################################################################### - -# Usually, the server key exchange does not contain any information -# that we are interested in. -# -# The exception is when we are using an ECDHE, DHE or DH-Anon suite. -# In this case, we can extract information about the chosen cipher from -# here. -type ServerKeyExchange(rec: SSLRecord) = case $context.connection.chosen_cipher() of { - TLS_ECDH_ECDSA_WITH_NULL_SHA, - TLS_ECDH_ECDSA_WITH_RC4_128_SHA, - TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_NULL_SHA, - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDH_RSA_WITH_NULL_SHA, - TLS_ECDH_RSA_WITH_RC4_128_SHA, - TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_NULL_SHA, - TLS_ECDHE_RSA_WITH_RC4_128_SHA, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDH_ANON_WITH_NULL_SHA, - TLS_ECDH_ANON_WITH_RC4_128_SHA, - TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA, - TLS_ECDH_ANON_WITH_AES_128_CBC_SHA, - TLS_ECDH_ANON_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_PSK_WITH_RC4_128_SHA, - TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_PSK_WITH_NULL_SHA, - TLS_ECDHE_PSK_WITH_NULL_SHA256, - TLS_ECDHE_PSK_WITH_NULL_SHA384, - TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, - TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, - TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, - TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, - TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, - TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, - TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, - TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, - TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, - TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, - TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, - TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, - TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, - TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, - TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, - TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, - TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, - TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, - TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, - TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, - TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, - TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, - TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, - TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, - TLS_ECDHE_ECDSA_WITH_AES_128_CCM, - TLS_ECDHE_ECDSA_WITH_AES_256_CCM, - TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, - TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 - -> ec_server_key_exchange : EcServerKeyExchange(rec); - - # DHE suites - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, - TLS_DHE_DSS_WITH_DES_CBC_SHA, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, - TLS_DHE_RSA_WITH_DES_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, - TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, - TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA, - TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, - TLS_DHE_DSS_WITH_RC4_128_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD, - TLS_DHE_DSS_WITH_AES_128_CBC_RMD, - TLS_DHE_DSS_WITH_AES_256_CBC_RMD, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD, - TLS_DHE_RSA_WITH_AES_128_CBC_RMD, - TLS_DHE_RSA_WITH_AES_256_CBC_RMD, - TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, - TLS_DHE_PSK_WITH_RC4_128_SHA, - TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA, - TLS_DHE_DSS_WITH_SEED_CBC_SHA, - TLS_DHE_RSA_WITH_SEED_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, - TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, - TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, - TLS_DHE_PSK_WITH_NULL_SHA256, - TLS_DHE_PSK_WITH_NULL_SHA384, - TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, - TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, - TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, - TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, - TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, - TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, - TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, - TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, - TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256, - TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384, - TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, - TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, - TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, - TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, - TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, - TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, - TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256, - TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384, - TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, - TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, - TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, - TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, - TLS_DHE_RSA_WITH_AES_128_CCM, - TLS_DHE_RSA_WITH_AES_256_CCM, - TLS_DHE_RSA_WITH_AES_128_CCM_8, - TLS_DHE_RSA_WITH_AES_256_CCM_8, - TLS_DHE_PSK_WITH_AES_128_CCM, - TLS_DHE_PSK_WITH_AES_256_CCM, - TLS_PSK_DHE_WITH_AES_128_CCM_8, - TLS_PSK_DHE_WITH_AES_256_CCM_8, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - # DH-anon suites - TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5, - TLS_DH_ANON_WITH_RC4_128_MD5, - TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA, - TLS_DH_ANON_WITH_DES_CBC_SHA, - TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA, - TLS_DH_ANON_WITH_AES_128_CBC_SHA, - TLS_DH_ANON_WITH_AES_256_CBC_SHA, - TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA, - TLS_DH_ANON_WITH_AES_128_CBC_SHA256, - TLS_DH_ANON_WITH_AES_256_CBC_SHA256, - TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA, - TLS_DH_ANON_WITH_SEED_CBC_SHA, - TLS_DH_ANON_WITH_AES_128_GCM_SHA256, - TLS_DH_ANON_WITH_AES_256_GCM_SHA384, - TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256, - TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256, - TLS_DH_ANON_WITH_ARIA_128_CBC_SHA256, - TLS_DH_ANON_WITH_ARIA_256_CBC_SHA384, - TLS_DH_ANON_WITH_ARIA_128_GCM_SHA256, - TLS_DH_ANON_WITH_ARIA_256_GCM_SHA384, - TLS_DH_ANON_WITH_CAMELLIA_128_GCM_SHA256, - TLS_DH_ANON_WITH_CAMELLIA_256_GCM_SHA384 - # DH non-anon suites do not send a ServerKeyExchange - -> dh_server_key_exchange : DhServerKeyExchange(rec); - - default - -> key : bytestring &restofdata &transient; -}; - -# For the moment, we really only are interested in the curve name. If it -# is not set (if the server sends explicit parameters), we do not bother. -# We also do not parse the actual signature data following the named curve. -type EcServerKeyExchange(rec: SSLRecord) = record { - curve_type: uint8; - curve: uint16; # only if curve_type = 3 (NAMED_CURVE) - data: bytestring &restofdata &transient; -}; - -# For both, dh_anon and dhe the ServerKeyExchange starts with a ServerDHParams -# structure. After that, they start to differ, but we do not care about that. -type DhServerKeyExchange(rec: SSLRecord) = record { - dh_p_length: uint16; - dh_p: bytestring &length=dh_p_length; - dh_g_length: uint16; - dh_g: bytestring &length=dh_g_length; - dh_Ys_length: uint16; - dh_Ys: bytestring &length=dh_Ys_length; - data: bytestring &restofdata &transient; -}; - - -###################################################################### -# V3 Certificate Request (7.4.4.) -###################################################################### - -# For now, ignore Certificate Request Details; just eat up message. -type CertificateRequest(rec: SSLRecord) = record { - cont : bytestring &restofdata &transient; -}; - - -###################################################################### -# V3 Server Hello Done (7.4.5.) -###################################################################### - -# Server Hello Done is empty -type ServerHelloDone(rec: SSLRecord) = empty; - - -###################################################################### -# V3 Client Certificate (7.4.6.) -###################################################################### - -# Client Certificate is identical to Server Certificate; -# no further definition here - - -###################################################################### -# V3 Client Key Exchange Message (7.4.7.) -###################################################################### - -# For now ignore details of ClientKeyExchange (most of it is -# encrypted anyway); just eat up message. -type ClientKeyExchange(rec: SSLRecord) = record { - key : bytestring &restofdata &transient; -}; - ###################################################################### # V2 Client Master Key (SSLv2 2.5.) ###################################################################### @@ -641,75 +117,6 @@ type V2ClientMasterKey(rec: SSLRecord) = record { }; -###################################################################### -# V3 Certificate Verify (7.4.8.) -###################################################################### - -# For now, ignore Certificate Verify; just eat up the message. -type CertificateVerify(rec: SSLRecord) = record { - cont : bytestring &restofdata &transient; -}; - - -###################################################################### -# V3 Finished (7.4.9.) -###################################################################### - -# The finished messages are always sent after encryption is in effect, -# so we will not be able to read those messages. -type Finished(rec: SSLRecord) = record { - cont : bytestring &restofdata &transient; -}; - -type SessionTicketHandshake(rec: SSLRecord) = record { - ticket_lifetime_hint: uint32; - data: bytestring &restofdata; -}; - -###################################################################### -# V3 Handshake Protocol (7.) -###################################################################### - -type UnknownHandshake(hs: Handshake, is_orig: bool) = record { - data : bytestring &restofdata &transient; -}; - -type Handshake(rec: SSLRecord) = record { - msg_type : uint8; - length : uint24; - - body : case msg_type of { - HELLO_REQUEST -> hello_request : HelloRequest(rec); - CLIENT_HELLO -> client_hello : ClientHello(rec); - SERVER_HELLO -> server_hello : ServerHello(rec); - SESSION_TICKET -> session_ticket : SessionTicketHandshake(rec); - CERTIFICATE -> certificate : Certificate(rec); - SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(rec); - CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(rec); - SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(rec); - CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(rec); - CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec); - FINISHED -> finished : Finished(rec); - CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient; - CERTIFICATE_STATUS -> certificate_status : CertificateStatus(rec); - default -> unknown_handshake : UnknownHandshake(this, rec.is_orig); - } &length = to_int()(length); -}; - - -###################################################################### -# Fragmentation (6.2.1.) -###################################################################### - -type UnknownRecord(rec: SSLRecord) = record { - cont : bytestring &restofdata &transient; -}; - -type CiphertextRecord(rec: SSLRecord) = record { - cont : bytestring &restofdata &transient; -}; - - ###################################################################### # initial datatype for binpac ###################################################################### @@ -725,28 +132,6 @@ type SSLPDU(is_orig: bool) = record { refine connection SSL_Conn += { - %member{ - int client_state_; - int server_state_; - int record_layer_version_; - uint32 chosen_cipher_; - %} - - %init{ - server_state_ = STATE_CLEAR; - client_state_ = STATE_CLEAR; - record_layer_version_ = UNKNOWN_VERSION; - chosen_cipher_ = NO_CHOSEN_CIPHER; - %} - - function chosen_cipher() : int %{ return chosen_cipher_; %} - - function set_cipher(cipher: uint32) : bool - %{ - chosen_cipher_ = cipher; - return true; - %} - function determine_ssl_record_layer(head0 : uint8, head1 : uint8, head2 : uint8, head3: uint8, head4: uint8, is_orig: bool) : int %{ @@ -818,24 +203,4 @@ refine connection SSL_Conn += { return UNKNOWN_VERSION; %} - function client_state() : int %{ return client_state_; %} - - function server_state() : int %{ return client_state_; %} - - function state(is_orig: bool) : int - %{ - if ( is_orig ) - return client_state_; - else - return server_state_; - %} - - function startEncryption(is_orig: bool) : bool - %{ - if ( is_orig ) - client_state_ = STATE_ENCRYPTED; - else - server_state_ = STATE_ENCRYPTED; - return true; - %} }; diff --git a/src/analyzer/protocol/ssl/ssl.pac b/src/analyzer/protocol/ssl/ssl.pac index 4a32227088..f7e7c17e7f 100644 --- a/src/analyzer/protocol/ssl/ssl.pac +++ b/src/analyzer/protocol/ssl/ssl.pac @@ -10,23 +10,32 @@ %extern{ #include "events.bif.h" + +namespace analyzer { namespace ssl { class SSL_Analyzer; } } +typedef analyzer::ssl::SSL_Analyzer* SSLAnalyzer; + +#include "SSL.h" %} +extern type SSLAnalyzer; + analyzer SSL withcontext { connection: SSL_Conn; flow: SSL_Flow; }; -connection SSL_Conn(bro_analyzer: BroAnalyzer) { +connection SSL_Conn(bro_analyzer: SSLAnalyzer) { upflow = SSL_Flow(true); downflow = SSL_Flow(false); }; +%include ssl-dtls-protocol.pac %include ssl-protocol.pac flow SSL_Flow(is_orig: bool) { flowunit = SSLPDU(is_orig) withcontext(connection, this); } +%include ssl-dtls-analyzer.pac %include ssl-analyzer.pac %include ssl-defs.pac diff --git a/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac b/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac new file mode 100644 index 0000000000..fb6ce2e7a4 --- /dev/null +++ b/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac @@ -0,0 +1,273 @@ +# Analyzer for SSL/TLS Handshake protocol (Bro-specific part). + +%extern{ +#include +#include +#include +#include + +#include "util.h" + +#include "file_analysis/Manager.h" +%} + +%header{ + class extract_certs { + public: + bytestring const& operator() (X509Certificate* cert) const + { + return cert->certificate(); + } + }; + + string orig_label(bool is_orig); + string handshake_type_label(int type); + %} + +refine connection Handshake_Conn += { + + %include proc-client-hello.pac + %include proc-server-hello.pac + %include proc-certificate.pac + + function proc_session_ticket_handshake(rec: SessionTicketHandshake, is_orig: bool): bool + %{ + if ( ssl_session_ticket_handshake ) + { + BifEvent::generate_ssl_session_ticket_handshake(bro_analyzer(), + bro_analyzer()->Conn(), + ${rec.ticket_lifetime_hint}, + new StringVal(${rec.data}.length(), (const char*) ${rec.data}.data())); + } + return true; + %} + + function proc_ssl_extension(rec: Handshake, type: int, sourcedata: const_bytestring) : bool + %{ + // We cheat a little bit here. We want to throw this event + // for every extension we encounter, even those that are + // handled by more specialized events later. To access the + // parsed data, we use sourcedata, which contains the whole + // data blob of the extension, including headers. We skip + // over those (4 bytes). + size_t length = sourcedata.length(); + if ( length < 4 ) + { + // This should be impossible due to the binpac parser + // and protocol description + bro_analyzer()->ProtocolViolation(fmt("Impossible extension length: %zu", length)); + bro_analyzer()->SetSkip(true); + return true; + } + + length -= 4; + const unsigned char* data = sourcedata.begin() + 4; + + if ( ssl_extension ) + BifEvent::generate_ssl_extension(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.is_orig}, type, + new StringVal(length, reinterpret_cast(data))); + return true; + %} + + function proc_ec_point_formats(rec: Handshake, point_format_list: uint8[]) : bool + %{ + VectorVal* points = new VectorVal(internal_type("index_vec")->AsVectorType()); + + if ( point_format_list ) + { + for ( unsigned int i = 0; i < point_format_list->size(); ++i ) + points->Assign(i, new Val((*point_format_list)[i], TYPE_COUNT)); + } + + BifEvent::generate_ssl_extension_ec_point_formats(bro_analyzer(), bro_analyzer()->Conn(), + ${rec.is_orig}, points); + + return true; + %} + + function proc_elliptic_curves(rec: Handshake, list: uint16[]) : bool + %{ + VectorVal* curves = new VectorVal(internal_type("index_vec")->AsVectorType()); + + if ( list ) + { + for ( unsigned int i = 0; i < list->size(); ++i ) + curves->Assign(i, new Val((*list)[i], TYPE_COUNT)); + } + + BifEvent::generate_ssl_extension_elliptic_curves(bro_analyzer(), bro_analyzer()->Conn(), + ${rec.is_orig}, curves); + + return true; + %} + + function proc_apnl(rec: Handshake, protocols: ProtocolName[]) : bool + %{ + VectorVal* plist = new VectorVal(internal_type("string_vec")->AsVectorType()); + + if ( protocols ) + { + for ( unsigned int i = 0; i < protocols->size(); ++i ) + plist->Assign(i, new StringVal((*protocols)[i]->name().length(), (const char*) (*protocols)[i]->name().data())); + } + + BifEvent::generate_ssl_extension_application_layer_protocol_negotiation(bro_analyzer(), bro_analyzer()->Conn(), + ${rec.is_orig}, plist); + + return true; + %} + + function proc_server_name(rec: Handshake, list: ServerName[]) : bool + %{ + VectorVal* servers = new VectorVal(internal_type("string_vec")->AsVectorType()); + + if ( list ) + { + for ( unsigned int i = 0, j = 0; i < list->size(); ++i ) + { + ServerName* servername = (*list)[i]; + if ( servername->name_type() != 0 ) + { + bro_analyzer()->Weird(fmt("Encountered unknown type in server name ssl extension: %d", servername->name_type())); + continue; + } + + if ( servername->host_name() ) + servers->Assign(j++, new StringVal(servername->host_name()->host_name().length(), (const char*) servername->host_name()->host_name().data())); + else + bro_analyzer()->Weird("Empty server_name extension in ssl connection"); + } + } + + BifEvent::generate_ssl_extension_server_name(bro_analyzer(), bro_analyzer()->Conn(), + ${rec.is_orig}, servers); + + return true; + %} + + function proc_v3_certificate(is_orig: bool, cl : X509Certificate[]) : bool + %{ + vector* certs = cl; + vector* cert_list = new vector(); + + std::transform(certs->begin(), certs->end(), + std::back_inserter(*cert_list), extract_certs()); + + bool ret = proc_certificate(is_orig, cert_list); + delete cert_list; + return ret; + %} + + function proc_unknown_handshake(hs: Handshake, is_orig: bool) : bool + %{ + bro_analyzer()->ProtocolViolation(fmt("unknown handshake message (%d) from %s", + ${hs.msg_type}, orig_label(is_orig).c_str())); + return true; + %} + + function proc_certificate_status(rec : Handshake, status_type: uint8, response: bytestring) : bool + %{ + if ( status_type == 1 ) // ocsp + { + BifEvent::generate_ssl_stapled_ocsp(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.is_orig}, + new StringVal(response.length(), + (const char*) response.data())); + } + + return true; + %} + + function proc_ec_server_key_exchange(rec: Handshake, curve_type: uint8, curve: uint16) : bool + %{ + if ( curve_type == NAMED_CURVE ) + BifEvent::generate_ssl_server_curve(bro_analyzer(), + bro_analyzer()->Conn(), curve); + + return true; + %} + + function proc_dh_server_key_exchange(rec: Handshake, p: bytestring, g: bytestring, Ys: bytestring) : bool + %{ + BifEvent::generate_ssl_dh_server_params(bro_analyzer(), + bro_analyzer()->Conn(), + new StringVal(p.length(), (const char*) p.data()), + new StringVal(g.length(), (const char*) g.data()), + new StringVal(Ys.length(), (const char*) Ys.data()) + ); + + return true; + %} + + function proc_handshake(is_orig: bool, msg_type: uint8, length: uint32) : bool + %{ + BifEvent::generate_ssl_handshake_message(bro_analyzer(), + bro_analyzer()->Conn(), is_orig, msg_type, length); + + return true; + %} + + +}; + +refine typeattr ClientHello += &let { + proc : bool = $context.connection.proc_client_hello(client_version, + gmt_unix_time, random_bytes, + session_id, csuits, 0); +}; + +refine typeattr ServerHello += &let { + proc : bool = $context.connection.proc_server_hello(server_version, + gmt_unix_time, random_bytes, session_id, cipher_suite, 0, + compression_method); +}; + +refine typeattr Certificate += &let { + proc : bool = $context.connection.proc_v3_certificate(rec.is_orig, certificates); +}; + +refine typeattr UnknownHandshake += &let { + proc : bool = $context.connection.proc_unknown_handshake(hs, is_orig); +}; + +refine typeattr SessionTicketHandshake += &let { + proc : bool = $context.connection.proc_session_ticket_handshake(this, rec.is_orig); +} + +refine typeattr SSLExtension += &let { + proc : bool = $context.connection.proc_ssl_extension(rec, type, sourcedata); +}; + +refine typeattr EcPointFormats += &let { + proc : bool = $context.connection.proc_ec_point_formats(rec, point_format_list); +}; + +refine typeattr EllipticCurves += &let { + proc : bool = $context.connection.proc_elliptic_curves(rec, elliptic_curve_list); +}; + +refine typeattr ApplicationLayerProtocolNegotiationExtension += &let { + proc : bool = $context.connection.proc_apnl(rec, protocol_name_list); +}; + +refine typeattr ServerNameExt += &let { + proc : bool = $context.connection.proc_server_name(rec, server_names); +}; + +refine typeattr CertificateStatus += &let { + proc : bool = $context.connection.proc_certificate_status(rec, status_type, response); +}; + +refine typeattr EcServerKeyExchange += &let { + proc : bool = $context.connection.proc_ec_server_key_exchange(rec, curve_type, curve); +}; + +refine typeattr DhServerKeyExchange += &let { + proc : bool = $context.connection.proc_dh_server_key_exchange(rec, dh_p, dh_g, dh_Ys); +}; + +refine typeattr Handshake += &let { + proc : bool = $context.connection.proc_handshake(is_orig, msg_type, msg_length); +}; + diff --git a/src/analyzer/protocol/ssl/tls-handshake-protocol.pac b/src/analyzer/protocol/ssl/tls-handshake-protocol.pac new file mode 100644 index 0000000000..723d95d7a4 --- /dev/null +++ b/src/analyzer/protocol/ssl/tls-handshake-protocol.pac @@ -0,0 +1,533 @@ +###################################################################### +# Handshake Protocols (7.) +###################################################################### + +enum HandshakeType { + HELLO_REQUEST = 0, + CLIENT_HELLO = 1, + SERVER_HELLO = 2, + SESSION_TICKET = 4, # RFC 5077 + CERTIFICATE = 11, + SERVER_KEY_EXCHANGE = 12, + CERTIFICATE_REQUEST = 13, + SERVER_HELLO_DONE = 14, + CERTIFICATE_VERIFY = 15, + CLIENT_KEY_EXCHANGE = 16, + FINISHED = 20, + CERTIFICATE_URL = 21, # RFC 3546 + CERTIFICATE_STATUS = 22, # RFC 3546 +}; + + +###################################################################### +# V3 Handshake Protocol (7.) +###################################################################### + +type UnknownHandshake(hs: Handshake, is_orig: bool) = record { + data : bytestring &restofdata &transient; +}; + +type Handshake(is_orig: bool) = record { + body : case msg_type of { + HELLO_REQUEST -> hello_request : HelloRequest(this); + CLIENT_HELLO -> client_hello : ClientHello(this); + SERVER_HELLO -> server_hello : ServerHello(this); + SESSION_TICKET -> session_ticket : SessionTicketHandshake(this); + CERTIFICATE -> certificate : Certificate(this); + SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(this); + CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(this); + SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(this); + CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(this); + CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(this); + FINISHED -> finished : Finished(this); + CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient; + CERTIFICATE_STATUS -> certificate_status : CertificateStatus(this); + default -> unknown_handshake : UnknownHandshake(this, is_orig); + } &length = msg_length; +} &byteorder = bigendian, + &let { + msg_type: uint8 = $context.connection.msg_type(); + msg_length: uint32 = $context.connection.msg_length(); +}; + +###################################################################### +# V3 Hello Request (7.4.1.1.) +###################################################################### + +# Hello Request is empty +type HelloRequest(rec: Handshake) = empty; + + +###################################################################### +# V3 Client Hello (7.4.1.2.) +###################################################################### + +type ClientHello(rec: Handshake) = record { + client_version : uint16; + gmt_unix_time : uint32; + random_bytes : bytestring &length = 28; + session_len : uint8; + session_id : uint8[session_len]; + csuit_len : uint16 &check(csuit_len > 1 && csuit_len % 2 == 0); + csuits : uint16[csuit_len/2]; + cmeth_len : uint8 &check(cmeth_len > 0); + cmeths : uint8[cmeth_len]; + # This weirdness is to deal with the possible existence or absence + # of the following fields. + ext_len: uint16[] &until($element == 0 || $element != 0); + extensions : SSLExtension(rec)[] &until($input.length() == 0); +}; + +###################################################################### +# V3 Server Hello (7.4.1.3.) +###################################################################### + +type ServerHello(rec: Handshake) = record { + server_version : uint16; + gmt_unix_time : uint32; + random_bytes : bytestring &length = 28; + session_len : uint8; + session_id : uint8[session_len]; + cipher_suite : uint16[1]; + compression_method : uint8; + # This weirdness is to deal with the possible existence or absence + # of the following fields. + ext_len: uint16[] &until($element == 0 || $element != 0); + extensions : SSLExtension(rec)[] &until($input.length() == 0); +} &let { + cipher_set : bool = + $context.connection.set_cipher(cipher_suite[0]); +}; + +###################################################################### +# V3 Server Certificate (7.4.2.) +###################################################################### + +type X509Certificate = record { + length : uint24; + certificate : bytestring &length = to_int()(length); +}; + +type Certificate(rec: Handshake) = record { + length : uint24; + certificates : X509Certificate[] &until($input.length() == 0); +} &length = to_int()(length)+3; + +# OCSP Stapling + +type CertificateStatus(rec: Handshake) = record { + status_type: uint8; # 1 = ocsp, everything else is undefined + length : uint24; + response: bytestring &restofdata; +}; + +###################################################################### +# V3 Server Key Exchange Message (7.4.3.) +###################################################################### + +# Usually, the server key exchange does not contain any information +# that we are interested in. +# +# The exception is when we are using an ECDHE, DHE or DH-Anon suite. +# In this case, we can extract information about the chosen cipher from +# here. +type ServerKeyExchange(rec: Handshake) = case $context.connection.chosen_cipher() of { + TLS_ECDH_ECDSA_WITH_NULL_SHA, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_NULL_SHA, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDH_RSA_WITH_NULL_SHA, + TLS_ECDH_RSA_WITH_RC4_128_SHA, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_NULL_SHA, + TLS_ECDHE_RSA_WITH_RC4_128_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDH_ANON_WITH_NULL_SHA, + TLS_ECDH_ANON_WITH_RC4_128_SHA, + TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_ANON_WITH_AES_128_CBC_SHA, + TLS_ECDH_ANON_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_PSK_WITH_RC4_128_SHA, + TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_PSK_WITH_NULL_SHA, + TLS_ECDHE_PSK_WITH_NULL_SHA256, + TLS_ECDHE_PSK_WITH_NULL_SHA384, + TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, + TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, + TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, + TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, + TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, + TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, + TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, + TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, + TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, + TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + -> ec_server_key_exchange : EcServerKeyExchange(rec); + + # DHE suites + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, + TLS_DHE_DSS_WITH_DES_CBC_SHA, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, + TLS_DHE_RSA_WITH_DES_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA, + TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, + TLS_DHE_DSS_WITH_RC4_128_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD, + TLS_DHE_DSS_WITH_AES_128_CBC_RMD, + TLS_DHE_DSS_WITH_AES_256_CBC_RMD, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD, + TLS_DHE_RSA_WITH_AES_128_CBC_RMD, + TLS_DHE_RSA_WITH_AES_256_CBC_RMD, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, + TLS_DHE_PSK_WITH_RC4_128_SHA, + TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + TLS_DHE_DSS_WITH_SEED_CBC_SHA, + TLS_DHE_RSA_WITH_SEED_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + TLS_DHE_PSK_WITH_NULL_SHA256, + TLS_DHE_PSK_WITH_NULL_SHA384, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, + TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, + TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, + TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, + TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, + TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, + TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256, + TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384, + TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, + TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, + TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, + TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, + TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256, + TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384, + TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, + TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, + TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + TLS_DHE_RSA_WITH_AES_128_CCM, + TLS_DHE_RSA_WITH_AES_256_CCM, + TLS_DHE_RSA_WITH_AES_128_CCM_8, + TLS_DHE_RSA_WITH_AES_256_CCM_8, + TLS_DHE_PSK_WITH_AES_128_CCM, + TLS_DHE_PSK_WITH_AES_256_CCM, + TLS_PSK_DHE_WITH_AES_128_CCM_8, + TLS_PSK_DHE_WITH_AES_256_CCM_8, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + # DH-anon suites + TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5, + TLS_DH_ANON_WITH_RC4_128_MD5, + TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA, + TLS_DH_ANON_WITH_DES_CBC_SHA, + TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA, + TLS_DH_ANON_WITH_AES_128_CBC_SHA, + TLS_DH_ANON_WITH_AES_256_CBC_SHA, + TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA, + TLS_DH_ANON_WITH_AES_128_CBC_SHA256, + TLS_DH_ANON_WITH_AES_256_CBC_SHA256, + TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA, + TLS_DH_ANON_WITH_SEED_CBC_SHA, + TLS_DH_ANON_WITH_AES_128_GCM_SHA256, + TLS_DH_ANON_WITH_AES_256_GCM_SHA384, + TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256, + TLS_DH_ANON_WITH_ARIA_128_CBC_SHA256, + TLS_DH_ANON_WITH_ARIA_256_CBC_SHA384, + TLS_DH_ANON_WITH_ARIA_128_GCM_SHA256, + TLS_DH_ANON_WITH_ARIA_256_GCM_SHA384, + TLS_DH_ANON_WITH_CAMELLIA_128_GCM_SHA256, + TLS_DH_ANON_WITH_CAMELLIA_256_GCM_SHA384 + # DH non-anon suites do not send a ServerKeyExchange + -> dh_server_key_exchange : DhServerKeyExchange(rec); + + default + -> key : bytestring &restofdata &transient; +}; + +# For the moment, we really only are interested in the curve name. If it +# is not set (if the server sends explicit parameters), we do not bother. +# We also do not parse the actual signature data following the named curve. +type EcServerKeyExchange(rec: Handshake) = record { + curve_type: uint8; + curve: uint16; # only if curve_type = 3 (NAMED_CURVE) + data: bytestring &restofdata &transient; +}; + +# For both, dh_anon and dhe the ServerKeyExchange starts with a ServerDHParams +# structure. After that, they start to differ, but we do not care about that. +type DhServerKeyExchange(rec: Handshake) = record { + dh_p_length: uint16; + dh_p: bytestring &length=dh_p_length; + dh_g_length: uint16; + dh_g: bytestring &length=dh_g_length; + dh_Ys_length: uint16; + dh_Ys: bytestring &length=dh_Ys_length; + data: bytestring &restofdata &transient; +}; + + +###################################################################### +# V3 Certificate Request (7.4.4.) +###################################################################### + +# For now, ignore Certificate Request Details; just eat up message. +type CertificateRequest(rec: Handshake) = record { + cont : bytestring &restofdata &transient; +}; + + +###################################################################### +# V3 Server Hello Done (7.4.5.) +###################################################################### + +# Server Hello Done is empty +type ServerHelloDone(rec: Handshake) = empty; + + +###################################################################### +# V3 Client Certificate (7.4.6.) +###################################################################### + +# Client Certificate is identical to Server Certificate; +# no further definition here + + +###################################################################### +# V3 Client Key Exchange Message (7.4.7.) +###################################################################### + +# For now ignore details of ClientKeyExchange (most of it is +# encrypted anyway); just eat up message. +type ClientKeyExchange(rec: Handshake) = record { + key : bytestring &restofdata &transient; +}; + + +###################################################################### +# V3 Certificate Verify (7.4.8.) +###################################################################### + +# For now, ignore Certificate Verify; just eat up the message. +type CertificateVerify(rec: Handshake) = record { + cont : bytestring &restofdata &transient; +}; + + +###################################################################### +# V3 Finished (7.4.9.) +###################################################################### + +# The finished messages are always sent after encryption is in effect, +# so we will not be able to read those messages. +type Finished(rec: Handshake) = record { + cont : bytestring &restofdata &transient; +}; + +type SessionTicketHandshake(rec: Handshake) = record { + ticket_lifetime_hint: uint32; + data: bytestring &restofdata; +}; + +###################################################################### +# TLS Extensions +###################################################################### + +type SSLExtension(rec: Handshake) = record { + type: uint16; + data_len: uint16; + + # Pretty code ahead. Deal with the fact that perhaps extensions are + # not really present and we do not want to fail because of that. + ext: case type of { + EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION -> apnl: ApplicationLayerProtocolNegotiationExtension(rec)[] &until($element == 0 || $element != 0); + EXT_ELLIPTIC_CURVES -> elliptic_curves: EllipticCurves(rec)[] &until($element == 0 || $element != 0); + EXT_EC_POINT_FORMATS -> ec_point_formats: EcPointFormats(rec)[] &until($element == 0 || $element != 0); +# EXT_STATUS_REQUEST -> status_request: StatusRequest(rec)[] &until($element == 0 || $element != 0); + EXT_SERVER_NAME -> server_name: ServerNameExt(rec)[] &until($element == 0 || $element != 0); + default -> data: bytestring &restofdata; + }; +} &length=data_len+4 &exportsourcedata; + +type ServerNameHostName() = record { + length: uint16; + host_name: bytestring &length=length; +}; + +type ServerName() = record { + name_type: uint8; # has to be 0 for host-name + name: case name_type of { + 0 -> host_name: ServerNameHostName; + default -> data : bytestring &restofdata &transient; # unknown name + }; +}; + +type ServerNameExt(rec: Handshake) = record { + length: uint16; + server_names: ServerName[] &until($input.length() == 0); +} &length=length+2; + +# Do not parse for now. Structure is correct, but only contains asn.1 data that we would not use further. +#type OcspStatusRequest(rec: Handshake) = record { +# responder_id_list_length: uint16; +# responder_id_list: bytestring &length=responder_id_list_length; +# request_extensions_length: uint16; +# request_extensions: bytestring &length=request_extensions_length; +#}; +# +#type StatusRequest(rec: Handshake) = record { +# status_type: uint8; # 1 -> ocsp +# req: case status_type of { +# 1 -> ocsp_status_request: OcspStatusRequest(rec); +# default -> data : bytestring &restofdata &transient; # unknown +# }; +#}; + +type EcPointFormats(rec: Handshake) = record { + length: uint8; + point_format_list: uint8[length]; +}; + +type EllipticCurves(rec: Handshake) = record { + length: uint16; + elliptic_curve_list: uint16[length/2]; +}; + +type ProtocolName() = record { + length: uint8; + name: bytestring &length=length; +}; + +type ApplicationLayerProtocolNegotiationExtension(rec: Handshake) = record { + length: uint16; + protocol_name_list: ProtocolName[] &until($input.length() == 0); +} &length=length+2; + +refine connection Handshake_Conn += { + + %member{ + uint32 chosen_cipher_; + uint8 msg_type_; + uint32 msg_length_; + %} + + %init{ + chosen_cipher_ = NO_CHOSEN_CIPHER; + msg_type_ = 0; + msg_length_ = 0; + %} + + function chosen_cipher() : int %{ return chosen_cipher_; %} + + function msg_type() : uint8 %{ return msg_type_; %} + + function msg_length() : uint32 %{ return msg_length_; %} + + function set_msg_type(type: uint8) : bool + %{ + msg_type_ = type; + return true; + %} + + function set_msg_length(len: uint32) : bool + %{ + msg_length_ = len; + return true; + %} + + function set_cipher(cipher: uint32) : bool + %{ + chosen_cipher_ = cipher; + return true; + %} +}; + + diff --git a/src/analyzer/protocol/ssl/tls-handshake.pac b/src/analyzer/protocol/ssl/tls-handshake.pac new file mode 100644 index 0000000000..a1bd2e3954 --- /dev/null +++ b/src/analyzer/protocol/ssl/tls-handshake.pac @@ -0,0 +1,24 @@ +# Binpac analyzer just for the TLS handshake protocol and nothing else + +%include binpac.pac +%include bro.pac + +analyzer TLSHandshake withcontext { + connection: Handshake_Conn; + flow: Handshake_Flow; +}; + +connection Handshake_Conn(bro_analyzer: BroAnalyzer) { + upflow = Handshake_Flow(true); + downflow = Handshake_Flow(false); +}; + +flow Handshake_Flow(is_orig: bool) { + datagram = Handshake(is_orig) withcontext(connection, this); +} + +%include tls-handshake-protocol.pac +%include tls-handshake-analyzer.pac + +%include ssl-defs.pac + diff --git a/testing/btest/Traces/tls/dtls-openssl.pcap b/testing/btest/Traces/tls/dtls-openssl.pcap new file mode 100644 index 0000000000..b07e6921a1 Binary files /dev/null and b/testing/btest/Traces/tls/dtls-openssl.pcap differ