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.
This commit is contained in:
Johanna Amann 2015-03-11 15:53:32 -07:00
parent 1ae97e7e62
commit 47de906612
4 changed files with 67 additions and 62 deletions

View file

@ -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) 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 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); handshake_interp->NewData(orig, begin, end);
} }
catch ( const binpac::Exception& e ) catch ( const binpac::Exception& e )

View file

@ -42,7 +42,7 @@ refine connection Handshake_Conn += {
return true; 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 // We cheat a little bit here. We want to throw this event
// for every extension we encounter, even those that are // for every extension we encounter, even those that are
@ -70,7 +70,7 @@ refine connection Handshake_Conn += {
return true; 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()); VectorVal* points = new VectorVal(internal_type("index_vec")->AsVectorType());
@ -86,7 +86,7 @@ refine connection Handshake_Conn += {
return true; 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()); VectorVal* curves = new VectorVal(internal_type("index_vec")->AsVectorType());
@ -102,7 +102,7 @@ refine connection Handshake_Conn += {
return true; 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()); VectorVal* plist = new VectorVal(internal_type("string_vec")->AsVectorType());
@ -118,7 +118,7 @@ refine connection Handshake_Conn += {
return true; 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()); VectorVal* servers = new VectorVal(internal_type("string_vec")->AsVectorType());
@ -159,14 +159,14 @@ refine connection Handshake_Conn += {
return ret; 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", bro_analyzer()->ProtocolViolation(fmt("unknown handshake message (%d) from %s",
${hs.msg_type}, orig_label(is_orig).c_str())); ${hs.msg_type}, orig_label(is_orig).c_str()));
return true; 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 if ( status_type == 1 ) // ocsp
{ {
@ -179,7 +179,7 @@ refine connection Handshake_Conn += {
return true; 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 ) if ( curve_type == NAMED_CURVE )
BifEvent::generate_ssl_server_curve(bro_analyzer(), BifEvent::generate_ssl_server_curve(bro_analyzer(),
@ -188,7 +188,7 @@ refine connection Handshake_Conn += {
return true; 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(), BifEvent::generate_ssl_dh_server_params(bro_analyzer(),
bro_analyzer()->Conn(), bro_analyzer()->Conn(),
@ -268,6 +268,6 @@ refine typeattr DhServerKeyExchange += &let {
}; };
refine typeattr Handshake += &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);
}; };

View file

@ -23,31 +23,36 @@ enum HandshakeType {
# V3 Handshake Protocol (7.) # V3 Handshake Protocol (7.)
###################################################################### ######################################################################
type UnknownHandshake(hs: Handshake, is_orig: bool) = record { type HandshakeRecord(is_orig: bool) = record {
data : bytestring &restofdata &transient; msg_type: uint8;
}; msg_length: uint32;
rec: Handshake(this);
# rec: bytestring &length=10 &transient;
} &length=(msg_length + 5);
type Handshake(is_orig: bool) = record { type Handshake(rec: HandshakeRecord) = case rec.msg_type of {
body : case msg_type of { HELLO_REQUEST -> hello_request : HelloRequest(rec);
HELLO_REQUEST -> hello_request : HelloRequest(this); CLIENT_HELLO -> client_hello : ClientHello(rec);
CLIENT_HELLO -> client_hello : ClientHello(this); SERVER_HELLO -> server_hello : ServerHello(rec);
SERVER_HELLO -> server_hello : ServerHello(this); SESSION_TICKET -> session_ticket : SessionTicketHandshake(rec);
SESSION_TICKET -> session_ticket : SessionTicketHandshake(this); CERTIFICATE -> certificate : Certificate(rec);
CERTIFICATE -> certificate : Certificate(this); SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(rec);
SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(this); CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(rec);
CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(this); SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(rec);
SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(this); CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(rec);
CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(this); CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(this); FINISHED -> finished : Finished(rec);
FINISHED -> finished : Finished(this); CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient;
CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient; CERTIFICATE_STATUS -> certificate_status : CertificateStatus(rec);
CERTIFICATE_STATUS -> certificate_status : CertificateStatus(this); default -> unknown_handshake : UnknownHandshake(rec, rec.is_orig);
default -> unknown_handshake : UnknownHandshake(this, is_orig); }
} &length = msg_length;
} &byteorder = bigendian, type HandshakePDU(is_orig: bool) = record {
&let { records: HandshakeRecord(is_orig)[] &transient;
msg_type: uint8 = $context.connection.msg_type(); } &byteorder = bigendian;
msg_length: uint32 = $context.connection.msg_length();
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 # Hello Request is empty
type HelloRequest(rec: Handshake) = empty; type HelloRequest(rec: HandshakeRecord) = empty;
###################################################################### ######################################################################
# V3 Client Hello (7.4.1.2.) # V3 Client Hello (7.4.1.2.)
###################################################################### ######################################################################
type ClientHello(rec: Handshake) = record { type ClientHello(rec: HandshakeRecord) = record {
client_version : uint16; client_version : uint16;
gmt_unix_time : uint32; gmt_unix_time : uint32;
random_bytes : bytestring &length = 28; random_bytes : bytestring &length = 28;
@ -82,7 +87,7 @@ type ClientHello(rec: Handshake) = record {
# V3 Server Hello (7.4.1.3.) # V3 Server Hello (7.4.1.3.)
###################################################################### ######################################################################
type ServerHello(rec: Handshake) = record { type ServerHello(rec: HandshakeRecord) = record {
server_version : uint16; server_version : uint16;
gmt_unix_time : uint32; gmt_unix_time : uint32;
random_bytes : bytestring &length = 28; random_bytes : bytestring &length = 28;
@ -108,14 +113,14 @@ type X509Certificate = record {
certificate : bytestring &length = to_int()(length); certificate : bytestring &length = to_int()(length);
}; };
type Certificate(rec: Handshake) = record { type Certificate(rec: HandshakeRecord) = record {
length : uint24; length : uint24;
certificates : X509Certificate[] &until($input.length() == 0); certificates : X509Certificate[] &until($input.length() == 0);
} &length = to_int()(length)+3; } &length = to_int()(length)+3;
# OCSP Stapling # OCSP Stapling
type CertificateStatus(rec: Handshake) = record { type CertificateStatus(rec: HandshakeRecord) = record {
status_type: uint8; # 1 = ocsp, everything else is undefined status_type: uint8; # 1 = ocsp, everything else is undefined
length : uint24; length : uint24;
response: bytestring &restofdata; 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. # 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 # In this case, we can extract information about the chosen cipher from
# here. # 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_NULL_SHA,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_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 # 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. # 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. # 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_type: uint8;
curve: uint16; # only if curve_type = 3 (NAMED_CURVE) curve: uint16; # only if curve_type = 3 (NAMED_CURVE)
data: bytestring &restofdata &transient; 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 # 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. # 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_length: uint16;
dh_p: bytestring &length=dh_p_length; dh_p: bytestring &length=dh_p_length;
dh_g_length: uint16; dh_g_length: uint16;
@ -360,7 +365,7 @@ type DhServerKeyExchange(rec: Handshake) = record {
###################################################################### ######################################################################
# For now, ignore Certificate Request Details; just eat up message. # For now, ignore Certificate Request Details; just eat up message.
type CertificateRequest(rec: Handshake) = record { type CertificateRequest(rec: HandshakeRecord) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
}; };
@ -370,7 +375,7 @@ type CertificateRequest(rec: Handshake) = record {
###################################################################### ######################################################################
# Server Hello Done is empty # 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 # For now ignore details of ClientKeyExchange (most of it is
# encrypted anyway); just eat up message. # encrypted anyway); just eat up message.
type ClientKeyExchange(rec: Handshake) = record { type ClientKeyExchange(rec: HandshakeRecord) = record {
key : bytestring &restofdata &transient; key : bytestring &restofdata &transient;
}; };
@ -397,7 +402,7 @@ type ClientKeyExchange(rec: Handshake) = record {
###################################################################### ######################################################################
# For now, ignore Certificate Verify; just eat up the message. # For now, ignore Certificate Verify; just eat up the message.
type CertificateVerify(rec: Handshake) = record { type CertificateVerify(rec: HandshakeRecord) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
}; };
@ -408,11 +413,11 @@ type CertificateVerify(rec: Handshake) = record {
# The finished messages are always sent after encryption is in effect, # The finished messages are always sent after encryption is in effect,
# so we will not be able to read those messages. # so we will not be able to read those messages.
type Finished(rec: Handshake) = record { type Finished(rec: HandshakeRecord) = record {
cont : bytestring &restofdata &transient; cont : bytestring &restofdata &transient;
}; };
type SessionTicketHandshake(rec: Handshake) = record { type SessionTicketHandshake(rec: HandshakeRecord) = record {
ticket_lifetime_hint: uint32; ticket_lifetime_hint: uint32;
data: bytestring &restofdata; data: bytestring &restofdata;
}; };
@ -421,7 +426,7 @@ type SessionTicketHandshake(rec: Handshake) = record {
# TLS Extensions # TLS Extensions
###################################################################### ######################################################################
type SSLExtension(rec: Handshake) = record { type SSLExtension(rec: HandshakeRecord) = record {
type: uint16; type: uint16;
data_len: uint16; data_len: uint16;
@ -450,20 +455,20 @@ type ServerName() = record {
}; };
}; };
type ServerNameExt(rec: Handshake) = record { type ServerNameExt(rec: HandshakeRecord) = record {
length: uint16; length: uint16;
server_names: ServerName[] &until($input.length() == 0); server_names: ServerName[] &until($input.length() == 0);
} &length=length+2; } &length=length+2;
# Do not parse for now. Structure is correct, but only contains asn.1 data that we would not use further. # 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_length: uint16;
# responder_id_list: bytestring &length=responder_id_list_length; # responder_id_list: bytestring &length=responder_id_list_length;
# request_extensions_length: uint16; # request_extensions_length: uint16;
# request_extensions: bytestring &length=request_extensions_length; # request_extensions: bytestring &length=request_extensions_length;
#}; #};
# #
#type StatusRequest(rec: Handshake) = record { #type StatusRequest(rec: HandshakeRecord) = record {
# status_type: uint8; # 1 -> ocsp # status_type: uint8; # 1 -> ocsp
# req: case status_type of { # req: case status_type of {
# 1 -> ocsp_status_request: OcspStatusRequest(rec); # 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; length: uint8;
point_format_list: uint8[length]; point_format_list: uint8[length];
}; };
type EllipticCurves(rec: Handshake) = record { type EllipticCurves(rec: HandshakeRecord) = record {
length: uint16; length: uint16;
elliptic_curve_list: uint16[length/2]; elliptic_curve_list: uint16[length/2];
}; };
@ -486,7 +491,7 @@ type ProtocolName() = record {
name: bytestring &length=length; name: bytestring &length=length;
}; };
type ApplicationLayerProtocolNegotiationExtension(rec: Handshake) = record { type ApplicationLayerProtocolNegotiationExtension(rec: HandshakeRecord) = record {
length: uint16; length: uint16;
protocol_name_list: ProtocolName[] &until($input.length() == 0); protocol_name_list: ProtocolName[] &until($input.length() == 0);
} &length=length+2; } &length=length+2;
@ -509,7 +514,7 @@ refine connection Handshake_Conn += {
function msg_type() : uint8 %{ return msg_type_; %} 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 function set_msg_type(type: uint8) : bool
%{ %{

View file

@ -13,12 +13,11 @@ connection Handshake_Conn(bro_analyzer: BroAnalyzer) {
downflow = Handshake_Flow(false); downflow = Handshake_Flow(false);
}; };
%include tls-handshake-protocol.pac
flow Handshake_Flow(is_orig: bool) { 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 tls-handshake-analyzer.pac
%include ssl-defs.pac %include ssl-defs.pac