From 47de9066126b5f7d33d59b4ac0198b880e464fbc Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Wed, 11 Mar 2015 15:53:32 -0700 Subject: [PATCH] Make handshake analyzer flow-based. This means we can feed data to it in chunks, which makes dealing with fragmentation a little bit more convenient. --- src/analyzer/protocol/ssl/SSL.cc | 5 +- .../protocol/ssl/tls-handshake-analyzer.pac | 20 ++-- .../protocol/ssl/tls-handshake-protocol.pac | 97 ++++++++++--------- src/analyzer/protocol/ssl/tls-handshake.pac | 7 +- 4 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/analyzer/protocol/ssl/SSL.cc b/src/analyzer/protocol/ssl/SSL.cc index a26807c14b..17df73bd6e 100644 --- a/src/analyzer/protocol/ssl/SSL.cc +++ b/src/analyzer/protocol/ssl/SSL.cc @@ -63,10 +63,11 @@ 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, (const unsigned char*) &msg_type, (const unsigned char*) &msg_type + 1); + uint32 host_length = htonl(length); + handshake_interp->NewData(orig, (const unsigned char*) &host_length, (const unsigned char*) &host_length + sizeof(host_length)); handshake_interp->NewData(orig, begin, end); } catch ( const binpac::Exception& e ) diff --git a/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac b/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac index fb6ce2e7a4..a52381189b 100644 --- a/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac +++ b/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac @@ -42,7 +42,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_ssl_extension(rec: Handshake, type: int, sourcedata: const_bytestring) : bool + function proc_ssl_extension(rec: HandshakeRecord, 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 @@ -70,7 +70,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_ec_point_formats(rec: Handshake, point_format_list: uint8[]) : bool + function proc_ec_point_formats(rec: HandshakeRecord, point_format_list: uint8[]) : bool %{ VectorVal* points = new VectorVal(internal_type("index_vec")->AsVectorType()); @@ -86,7 +86,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_elliptic_curves(rec: Handshake, list: uint16[]) : bool + function proc_elliptic_curves(rec: HandshakeRecord, list: uint16[]) : bool %{ VectorVal* curves = new VectorVal(internal_type("index_vec")->AsVectorType()); @@ -102,7 +102,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_apnl(rec: Handshake, protocols: ProtocolName[]) : bool + function proc_apnl(rec: HandshakeRecord, protocols: ProtocolName[]) : bool %{ VectorVal* plist = new VectorVal(internal_type("string_vec")->AsVectorType()); @@ -118,7 +118,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_server_name(rec: Handshake, list: ServerName[]) : bool + function proc_server_name(rec: HandshakeRecord, list: ServerName[]) : bool %{ VectorVal* servers = new VectorVal(internal_type("string_vec")->AsVectorType()); @@ -159,14 +159,14 @@ refine connection Handshake_Conn += { return ret; %} - function proc_unknown_handshake(hs: Handshake, is_orig: bool) : bool + function proc_unknown_handshake(hs: HandshakeRecord, 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 + function proc_certificate_status(rec : HandshakeRecord, status_type: uint8, response: bytestring) : bool %{ if ( status_type == 1 ) // ocsp { @@ -179,7 +179,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_ec_server_key_exchange(rec: Handshake, curve_type: uint8, curve: uint16) : bool + function proc_ec_server_key_exchange(rec: HandshakeRecord, curve_type: uint8, curve: uint16) : bool %{ if ( curve_type == NAMED_CURVE ) BifEvent::generate_ssl_server_curve(bro_analyzer(), @@ -188,7 +188,7 @@ refine connection Handshake_Conn += { return true; %} - function proc_dh_server_key_exchange(rec: Handshake, p: bytestring, g: bytestring, Ys: bytestring) : bool + function proc_dh_server_key_exchange(rec: HandshakeRecord, p: bytestring, g: bytestring, Ys: bytestring) : bool %{ BifEvent::generate_ssl_dh_server_params(bro_analyzer(), bro_analyzer()->Conn(), @@ -268,6 +268,6 @@ refine typeattr DhServerKeyExchange += &let { }; refine typeattr Handshake += &let { - proc : bool = $context.connection.proc_handshake(is_orig, msg_type, msg_length); + proc : bool = $context.connection.proc_handshake(rec.is_orig, rec.msg_type, rec.msg_length); }; diff --git a/src/analyzer/protocol/ssl/tls-handshake-protocol.pac b/src/analyzer/protocol/ssl/tls-handshake-protocol.pac index 723d95d7a4..25f890d089 100644 --- a/src/analyzer/protocol/ssl/tls-handshake-protocol.pac +++ b/src/analyzer/protocol/ssl/tls-handshake-protocol.pac @@ -23,31 +23,36 @@ enum HandshakeType { # V3 Handshake Protocol (7.) ###################################################################### -type UnknownHandshake(hs: Handshake, is_orig: bool) = record { - data : bytestring &restofdata &transient; -}; +type HandshakeRecord(is_orig: bool) = record { + msg_type: uint8; + msg_length: uint32; + rec: Handshake(this); +# rec: bytestring &length=10 &transient; +} &length=(msg_length + 5); -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(); +type Handshake(rec: HandshakeRecord) = case rec.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(rec, rec.is_orig); +} + +type HandshakePDU(is_orig: bool) = record { + records: HandshakeRecord(is_orig)[] &transient; +} &byteorder = bigendian; + +type UnknownHandshake(hs: HandshakeRecord, is_orig: bool) = record { + data : bytestring &restofdata &transient; }; ###################################################################### @@ -55,14 +60,14 @@ type Handshake(is_orig: bool) = record { ###################################################################### # Hello Request is empty -type HelloRequest(rec: Handshake) = empty; +type HelloRequest(rec: HandshakeRecord) = empty; ###################################################################### # V3 Client Hello (7.4.1.2.) ###################################################################### -type ClientHello(rec: Handshake) = record { +type ClientHello(rec: HandshakeRecord) = record { client_version : uint16; gmt_unix_time : uint32; random_bytes : bytestring &length = 28; @@ -82,7 +87,7 @@ type ClientHello(rec: Handshake) = record { # V3 Server Hello (7.4.1.3.) ###################################################################### -type ServerHello(rec: Handshake) = record { +type ServerHello(rec: HandshakeRecord) = record { server_version : uint16; gmt_unix_time : uint32; random_bytes : bytestring &length = 28; @@ -108,14 +113,14 @@ type X509Certificate = record { certificate : bytestring &length = to_int()(length); }; -type Certificate(rec: Handshake) = record { +type Certificate(rec: HandshakeRecord) = record { length : uint24; certificates : X509Certificate[] &until($input.length() == 0); } &length = to_int()(length)+3; # OCSP Stapling -type CertificateStatus(rec: Handshake) = record { +type CertificateStatus(rec: HandshakeRecord) = record { status_type: uint8; # 1 = ocsp, everything else is undefined length : uint24; response: bytestring &restofdata; @@ -131,7 +136,7 @@ type CertificateStatus(rec: Handshake) = record { # 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 { +type ServerKeyExchange(rec: HandshakeRecord) = 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, @@ -336,7 +341,7 @@ type ServerKeyExchange(rec: Handshake) = case $context.connection.chosen_cipher( # 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 { +type EcServerKeyExchange(rec: HandshakeRecord) = record { curve_type: uint8; curve: uint16; # only if curve_type = 3 (NAMED_CURVE) data: bytestring &restofdata &transient; @@ -344,7 +349,7 @@ type EcServerKeyExchange(rec: Handshake) = record { # 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 { +type DhServerKeyExchange(rec: HandshakeRecord) = record { dh_p_length: uint16; dh_p: bytestring &length=dh_p_length; dh_g_length: uint16; @@ -360,7 +365,7 @@ type DhServerKeyExchange(rec: Handshake) = record { ###################################################################### # For now, ignore Certificate Request Details; just eat up message. -type CertificateRequest(rec: Handshake) = record { +type CertificateRequest(rec: HandshakeRecord) = record { cont : bytestring &restofdata &transient; }; @@ -370,7 +375,7 @@ type CertificateRequest(rec: Handshake) = record { ###################################################################### # Server Hello Done is empty -type ServerHelloDone(rec: Handshake) = empty; +type ServerHelloDone(rec: HandshakeRecord) = empty; ###################################################################### @@ -387,7 +392,7 @@ type ServerHelloDone(rec: Handshake) = empty; # For now ignore details of ClientKeyExchange (most of it is # encrypted anyway); just eat up message. -type ClientKeyExchange(rec: Handshake) = record { +type ClientKeyExchange(rec: HandshakeRecord) = record { key : bytestring &restofdata &transient; }; @@ -397,7 +402,7 @@ type ClientKeyExchange(rec: Handshake) = record { ###################################################################### # For now, ignore Certificate Verify; just eat up the message. -type CertificateVerify(rec: Handshake) = record { +type CertificateVerify(rec: HandshakeRecord) = record { cont : bytestring &restofdata &transient; }; @@ -408,11 +413,11 @@ type CertificateVerify(rec: Handshake) = record { # 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 { +type Finished(rec: HandshakeRecord) = record { cont : bytestring &restofdata &transient; }; -type SessionTicketHandshake(rec: Handshake) = record { +type SessionTicketHandshake(rec: HandshakeRecord) = record { ticket_lifetime_hint: uint32; data: bytestring &restofdata; }; @@ -421,7 +426,7 @@ type SessionTicketHandshake(rec: Handshake) = record { # TLS Extensions ###################################################################### -type SSLExtension(rec: Handshake) = record { +type SSLExtension(rec: HandshakeRecord) = record { type: uint16; data_len: uint16; @@ -450,20 +455,20 @@ type ServerName() = record { }; }; -type ServerNameExt(rec: Handshake) = record { +type ServerNameExt(rec: HandshakeRecord) = 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 { +#type OcspStatusRequest(rec: HandshakeRecord) = 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 { +#type StatusRequest(rec: HandshakeRecord) = record { # status_type: uint8; # 1 -> ocsp # req: case status_type of { # 1 -> ocsp_status_request: OcspStatusRequest(rec); @@ -471,12 +476,12 @@ type ServerNameExt(rec: Handshake) = record { # }; #}; -type EcPointFormats(rec: Handshake) = record { +type EcPointFormats(rec: HandshakeRecord) = record { length: uint8; point_format_list: uint8[length]; }; -type EllipticCurves(rec: Handshake) = record { +type EllipticCurves(rec: HandshakeRecord) = record { length: uint16; elliptic_curve_list: uint16[length/2]; }; @@ -486,7 +491,7 @@ type ProtocolName() = record { name: bytestring &length=length; }; -type ApplicationLayerProtocolNegotiationExtension(rec: Handshake) = record { +type ApplicationLayerProtocolNegotiationExtension(rec: HandshakeRecord) = record { length: uint16; protocol_name_list: ProtocolName[] &until($input.length() == 0); } &length=length+2; @@ -509,7 +514,7 @@ refine connection Handshake_Conn += { function msg_type() : uint8 %{ return msg_type_; %} - function msg_length() : uint32 %{ return msg_length_; %} + function msg_length() : uint32 %{ fprintf(stderr, "Got length %d\n", msg_length_); return msg_length_; %} function set_msg_type(type: uint8) : bool %{ diff --git a/src/analyzer/protocol/ssl/tls-handshake.pac b/src/analyzer/protocol/ssl/tls-handshake.pac index a1bd2e3954..36d6999557 100644 --- a/src/analyzer/protocol/ssl/tls-handshake.pac +++ b/src/analyzer/protocol/ssl/tls-handshake.pac @@ -13,12 +13,11 @@ connection Handshake_Conn(bro_analyzer: BroAnalyzer) { downflow = Handshake_Flow(false); }; +%include tls-handshake-protocol.pac + flow Handshake_Flow(is_orig: bool) { - datagram = Handshake(is_orig) withcontext(connection, this); + flowunit = HandshakePDU(is_orig) withcontext(connection, this); } -%include tls-handshake-protocol.pac %include tls-handshake-analyzer.pac - %include ssl-defs.pac -