mirror of
https://github.com/zeek/zeek.git
synced 2025-10-14 20:48:21 +00:00
Merge remote-tracking branch 'origin/topic/bernhard/ssl-analyzer'
* origin/topic/bernhard/ssl-analyzer: Fix a few failing tests Add very basic ocsp stapling support. Add documentation, consts and tests for the new events. Support parsing of several TLS extensions. Make SSL/TLS version detection less brittle. Nicer notices for heartbleed. rip out state handline from ssl analyzer. enable detection of encrypted heartbleeds. also extract payload data in ssl_heartbeat add to local.bro, add disclaimer make tls heartbeat messages a bit better. fix tabs. polish script and probably detect encrypted attacks too. detect and alert on simple case of heartbleed default to TLS when not being able to determine version add is_orig to heartbeat event Throw new event for heartbeat messages. BIT-1178 #merged
This commit is contained in:
commit
201fc7b25a
28 changed files with 800 additions and 376 deletions
|
@ -34,26 +34,23 @@ type SSLRecord(is_orig: bool) = record {
|
|||
head4 : uint8;
|
||||
rec : RecordText(this)[] &length=length, &requires(content_type);
|
||||
} &length = length+5, &byteorder=bigendian,
|
||||
&let {
|
||||
&let {
|
||||
version : int =
|
||||
$context.connection.determine_ssl_version(head0, head1, head2);
|
||||
$context.connection.determine_ssl_record_layer(head0, head1, head2, head3, head4);
|
||||
|
||||
content_type : int = case version of {
|
||||
UNKNOWN_VERSION -> 0;
|
||||
SSLv20 -> head2+300;
|
||||
default -> head0;
|
||||
};
|
||||
|
||||
length : int = case version of {
|
||||
UNKNOWN_VERSION -> 0;
|
||||
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
|
||||
default -> (head3 << 8) | head4;
|
||||
};
|
||||
};
|
||||
|
||||
type RecordText(rec: SSLRecord) = case $context.connection.state() of {
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
|
||||
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
|
||||
type RecordText(rec: SSLRecord) = case $context.connection.state(rec.is_orig) of {
|
||||
STATE_ENCRYPTED
|
||||
-> ciphertext : CiphertextRecord(rec);
|
||||
default
|
||||
-> plaintext : PlaintextRecord(rec);
|
||||
|
@ -63,6 +60,7 @@ type PlaintextRecord(rec: SSLRecord) = case rec.content_type of {
|
|||
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
|
||||
ALERT -> alert : Alert(rec);
|
||||
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);
|
||||
|
@ -71,75 +69,98 @@ type PlaintextRecord(rec: SSLRecord) = case rec.content_type of {
|
|||
default -> unknown_record : UnknownRecord(rec);
|
||||
};
|
||||
|
||||
######################################################################
|
||||
# TLS Extensions
|
||||
######################################################################
|
||||
|
||||
type SSLExtension(rec: SSLRecord) = record {
|
||||
type: uint16;
|
||||
data_len: uint16;
|
||||
data: bytestring &length=data_len;
|
||||
|
||||
# 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; # 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; # 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;
|
||||
|
||||
######################################################################
|
||||
# state management according to Section 7.3. in spec
|
||||
# Encryption Tracking
|
||||
######################################################################
|
||||
|
||||
enum AnalyzerState {
|
||||
STATE_INITIAL,
|
||||
STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_IN_SERVER_HELLO,
|
||||
STATE_SERVER_HELLO_DONE,
|
||||
STATE_CLIENT_CERT,
|
||||
STATE_CLIENT_KEY_WITH_CERT,
|
||||
STATE_CLIENT_KEY_NO_CERT,
|
||||
STATE_CLIENT_CERT_VERIFIED,
|
||||
STATE_CLIENT_ENCRYPTED,
|
||||
STATE_CLIENT_FINISHED,
|
||||
STATE_ABBREV_SERVER_ENCRYPTED,
|
||||
STATE_ABBREV_SERVER_FINISHED,
|
||||
STATE_COMM_ENCRYPTED,
|
||||
STATE_CONN_ESTABLISHED,
|
||||
STATE_V2_CL_MASTER_KEY_EXPECTED,
|
||||
|
||||
STATE_TRACK_LOST,
|
||||
STATE_ANY
|
||||
STATE_CLEAR,
|
||||
STATE_ENCRYPTED
|
||||
};
|
||||
|
||||
%code{
|
||||
string state_label(int state_nr)
|
||||
{
|
||||
switch ( state_nr ) {
|
||||
case STATE_INITIAL:
|
||||
return string("INITIAL");
|
||||
case STATE_CLIENT_HELLO_RCVD:
|
||||
return string("CLIENT_HELLO_RCVD");
|
||||
case STATE_IN_SERVER_HELLO:
|
||||
return string("IN_SERVER_HELLO");
|
||||
case STATE_SERVER_HELLO_DONE:
|
||||
return string("SERVER_HELLO_DONE");
|
||||
case STATE_CLIENT_CERT:
|
||||
return string("CLIENT_CERT");
|
||||
case STATE_CLIENT_KEY_WITH_CERT:
|
||||
return string("CLIENT_KEY_WITH_CERT");
|
||||
case STATE_CLIENT_KEY_NO_CERT:
|
||||
return string("CLIENT_KEY_NO_CERT");
|
||||
case STATE_CLIENT_CERT_VERIFIED:
|
||||
return string("CLIENT_CERT_VERIFIED");
|
||||
case STATE_CLIENT_ENCRYPTED:
|
||||
return string("CLIENT_ENCRYPTED");
|
||||
case STATE_CLIENT_FINISHED:
|
||||
return string("CLIENT_FINISHED");
|
||||
case STATE_ABBREV_SERVER_ENCRYPTED:
|
||||
return string("ABBREV_SERVER_ENCRYPTED");
|
||||
case STATE_ABBREV_SERVER_FINISHED:
|
||||
return string("ABBREV_SERVER_FINISHED");
|
||||
case STATE_COMM_ENCRYPTED:
|
||||
return string("COMM_ENCRYPTED");
|
||||
case STATE_CONN_ESTABLISHED:
|
||||
return string("CONN_ESTABLISHED");
|
||||
case STATE_V2_CL_MASTER_KEY_EXPECTED:
|
||||
return string("STATE_V2_CL_MASTER_KEY_EXPECTED");
|
||||
case STATE_TRACK_LOST:
|
||||
return string("TRACK_LOST");
|
||||
case STATE_ANY:
|
||||
return string("ANY");
|
||||
case STATE_CLEAR:
|
||||
return string("CLEAR");
|
||||
|
||||
case STATE_ENCRYPTED:
|
||||
return string("ENCRYPTED");
|
||||
|
||||
default:
|
||||
return string(fmt("UNKNOWN (%d)", state_nr));
|
||||
|
@ -176,21 +197,7 @@ type ChangeCipherSpec(rec: SSLRecord) = record {
|
|||
type : uint8;
|
||||
} &length = 1, &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_CLIENT_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_CLIENT_KEY_NO_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CLIENT_CERT_VERIFIED,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CLIENT_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_ABBREV_SERVER_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.connection.lost_track();
|
||||
$context.connection.startEncryption(rec.is_orig);
|
||||
};
|
||||
|
||||
|
||||
|
@ -209,7 +216,7 @@ type Alert(rec: SSLRecord) = record {
|
|||
######################################################################
|
||||
|
||||
type V2Error(rec: SSLRecord) = record {
|
||||
data: bytestring &restofdata &transient;
|
||||
data : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
error_code : uint16 = ((rec.head3 << 8) | rec.head4);
|
||||
};
|
||||
|
@ -226,17 +233,21 @@ type ApplicationData(rec: SSLRecord) = record {
|
|||
};
|
||||
|
||||
######################################################################
|
||||
# Handshake Protocol (7.4.)
|
||||
# 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 &let {
|
||||
hr: bool = $context.connection.set_hello_requested(true);
|
||||
};
|
||||
type HelloRequest(rec: SSLRecord) = empty;
|
||||
|
||||
|
||||
######################################################################
|
||||
|
@ -257,16 +268,8 @@ type ClientHello(rec: SSLRecord) = record {
|
|||
# of the following fields.
|
||||
ext_len: uint16[] &until($element == 0 || $element != 0);
|
||||
extensions : SSLExtension(rec)[] &until($input.length() == 0);
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_INITIAL,
|
||||
STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
|
||||
($context.connection.hello_requested() &&
|
||||
$context.connection.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
|
||||
######################################################################
|
||||
# V2 Client Hello (SSLv2 2.5.)
|
||||
######################################################################
|
||||
|
@ -279,13 +282,6 @@ type V2ClientHello(rec: SSLRecord) = record {
|
|||
session_id : uint8[session_len];
|
||||
challenge : bytestring &length = chal_len;
|
||||
} &length = 6 + csuit_len + session_len + chal_len, &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_INITIAL,
|
||||
STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
|
||||
($context.connection.hello_requested() &&
|
||||
$context.connection.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
|
||||
$context.connection.lost_track();
|
||||
|
||||
client_version : int = rec.version;
|
||||
};
|
||||
|
||||
|
@ -306,11 +302,6 @@ type ServerHello(rec: SSLRecord) = record {
|
|||
# of the following fields.
|
||||
ext_len: uint16[] &until($element == 0 || $element != 0);
|
||||
extensions : SSLExtension(rec)[] &until($input.length() == 0);
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
|
||||
|
@ -329,14 +320,6 @@ type V2ServerHello(rec: SSLRecord) = record {
|
|||
ciphers : uint24[ciph_len/3];
|
||||
conn_id_data : bytestring &length = conn_id_len;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
(session_id_hit > 0 ?
|
||||
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, false) :
|
||||
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_V2_CL_MASTER_KEY_EXPECTED, rec.is_orig, false)) ||
|
||||
$context.connection.lost_track();
|
||||
|
||||
session_id_hit : uint8 = rec.head3;
|
||||
cert_type : uint8 = rec.head4;
|
||||
};
|
||||
|
@ -351,20 +334,18 @@ type X509Certificate = record {
|
|||
certificate : bytestring &length = to_int()(length);
|
||||
};
|
||||
|
||||
type CertificateList = X509Certificate[] &until($input.length() == 0);
|
||||
|
||||
type Certificate(rec: SSLRecord) = record {
|
||||
length : uint24;
|
||||
certificates : CertificateList &length = to_int()(length);
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_SERVER_HELLO_DONE,
|
||||
STATE_CLIENT_CERT, rec.is_orig, true) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
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.)
|
||||
|
@ -373,11 +354,6 @@ type Certificate(rec: SSLRecord) = record {
|
|||
# For now ignore details; just eat up complete message
|
||||
type ServerKeyExchange(rec: SSLRecord) = record {
|
||||
key : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
|
||||
|
@ -388,11 +364,6 @@ type ServerKeyExchange(rec: SSLRecord) = record {
|
|||
# For now, ignore Certificate Request Details; just eat up message.
|
||||
type CertificateRequest(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
|
||||
|
@ -401,12 +372,7 @@ type CertificateRequest(rec: SSLRecord) = record {
|
|||
######################################################################
|
||||
|
||||
# Server Hello Done is empty
|
||||
type ServerHelloDone(rec: SSLRecord) = empty &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_SERVER_HELLO_DONE, rec.is_orig, false) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
type ServerHelloDone(rec: SSLRecord) = empty;
|
||||
|
||||
|
||||
######################################################################
|
||||
|
@ -425,15 +391,6 @@ type ServerHelloDone(rec: SSLRecord) = empty &let {
|
|||
# encrypted anyway); just eat up message.
|
||||
type ClientKeyExchange(rec: SSLRecord) = record {
|
||||
key : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_SERVER_HELLO_DONE,
|
||||
STATE_CLIENT_KEY_NO_CERT, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CLIENT_CERT,
|
||||
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CLIENT_CERT,
|
||||
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
######################################################################
|
||||
|
@ -449,12 +406,10 @@ type V2ClientMasterKey(rec: SSLRecord) = record {
|
|||
en_key_data : bytestring &length = en_key_len &transient;
|
||||
key_arg_data : bytestring &length = key_arg_len &transient;
|
||||
} &length = 7 + cl_key_len + en_key_len + key_arg_len, &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_V2_CL_MASTER_KEY_EXPECTED,
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
|
||||
$context.connection.lost_track();
|
||||
|
||||
cipher_kind : int = (((rec.head3 << 16) | (rec.head4 << 8)) | cipher_kind_8);
|
||||
# encryption starts for both sides after this message.
|
||||
state_changed_client : bool = $context.connection.startEncryption(true);
|
||||
state_changed_server : bool = $context.connection.startEncryption(false);
|
||||
};
|
||||
|
||||
|
||||
|
@ -465,11 +420,6 @@ type V2ClientMasterKey(rec: SSLRecord) = record {
|
|||
# For now, ignore Certificate Verify; just eat up the message.
|
||||
type CertificateVerify(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||
STATE_CLIENT_CERT_VERIFIED, rec.is_orig, true) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
|
||||
|
@ -481,13 +431,6 @@ type CertificateVerify(rec: SSLRecord) = record {
|
|||
# so we will not be able to read those messages.
|
||||
type Finished(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_SERVER_HELLO_DONE,
|
||||
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CLIENT_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
type SessionTicketHandshake(rec: SSLRecord) = record {
|
||||
|
@ -499,10 +442,8 @@ type SessionTicketHandshake(rec: SSLRecord) = record {
|
|||
# V3 Handshake Protocol (7.)
|
||||
######################################################################
|
||||
|
||||
type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
|
||||
type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
|
||||
data : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool = $context.connection.lost_track();
|
||||
};
|
||||
|
||||
type Handshake(rec: SSLRecord) = record {
|
||||
|
@ -522,7 +463,7 @@ type Handshake(rec: SSLRecord) = record {
|
|||
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
|
||||
FINISHED -> finished : Finished(rec);
|
||||
CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient;
|
||||
CERTIFICATE_STATUS -> certificate_status : bytestring &restofdata &transient;
|
||||
CERTIFICATE_STATUS -> certificate_status : CertificateStatus(rec);
|
||||
default -> unknown_handshake : UnknownHandshake(this, rec.is_orig);
|
||||
} &length = to_int()(length);
|
||||
};
|
||||
|
@ -532,33 +473,12 @@ type Handshake(rec: SSLRecord) = record {
|
|||
# Fragmentation (6.2.1.)
|
||||
######################################################################
|
||||
|
||||
type UnknownRecord(rec: SSLRecord) = record {
|
||||
type UnknownRecord(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool = $context.connection.lost_track();
|
||||
};
|
||||
|
||||
type CiphertextRecord(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_CLIENT_FINISHED,
|
||||
STATE_CLIENT_FINISHED, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_CLIENT_FINISHED,
|
||||
STATE_CLIENT_FINISHED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_ABBREV_SERVER_ENCRYPTED,
|
||||
STATE_ABBREV_SERVER_FINISHED, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_CLIENT_ENCRYPTED,
|
||||
STATE_CLIENT_FINISHED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_COMM_ENCRYPTED,
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_COMM_ENCRYPTED,
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
|
||||
$context.connection.transition(STATE_CONN_ESTABLISHED,
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
|
||||
$context.connection.transition(STATE_CONN_ESTABLISHED,
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
|
||||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
|
||||
|
@ -578,67 +498,88 @@ type SSLPDU(is_orig: bool) = record {
|
|||
refine connection SSL_Conn += {
|
||||
|
||||
%member{
|
||||
int state_;
|
||||
int old_state_;
|
||||
bool hello_requested_;
|
||||
int client_state_;
|
||||
int server_state_;
|
||||
int record_layer_version_;
|
||||
%}
|
||||
|
||||
%init{
|
||||
state_ = STATE_INITIAL;
|
||||
old_state_ = STATE_INITIAL;
|
||||
hello_requested_ = false;
|
||||
server_state_ = STATE_CLEAR;
|
||||
client_state_ = STATE_CLEAR;
|
||||
record_layer_version_ = UNKNOWN_VERSION;
|
||||
%}
|
||||
|
||||
function determine_ssl_version(head0 : uint8, head1 : uint8,
|
||||
head2 : uint8) : int
|
||||
function determine_ssl_record_layer(head0 : uint8, head1 : uint8,
|
||||
head2 : uint8, head3: uint8, head4: uint8) : int
|
||||
%{
|
||||
if ( head0 >= 20 && head0 <= 23 &&
|
||||
head1 == 0x03 && head2 <= 0x03 )
|
||||
// This is most probably SSL version 3.
|
||||
return (head1 << 8) | head2;
|
||||
if ( record_layer_version_ != UNKNOWN_VERSION )
|
||||
return record_layer_version_;
|
||||
|
||||
else if ( head0 >= 128 && head2 < 5 && head2 != 3 )
|
||||
// Not very strong evidence, but we suspect
|
||||
// this to be SSLv2.
|
||||
return SSLv20;
|
||||
if ( head0 & 0x80 )
|
||||
{
|
||||
if ( head2 == 0x01 ) // SSLv2 client hello.
|
||||
{
|
||||
uint16 version = (head3 << 8) | head4;
|
||||
if ( version != SSLv20 && version != SSLv30 && version != TLSv10 &&
|
||||
version != TLSv11 && version != TLSv12 )
|
||||
{
|
||||
bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL client hello. Version: %d", version));
|
||||
return UNKNOWN_VERSION;
|
||||
}
|
||||
|
||||
else
|
||||
else
|
||||
return SSLv20;
|
||||
}
|
||||
|
||||
else if ( head2 == 0x04 ) // SSLv2 server hello. This connection will continue using SSLv2.
|
||||
{
|
||||
record_layer_version_ = SSLv20;
|
||||
return SSLv20;
|
||||
}
|
||||
|
||||
else // this is not SSL or TLS.
|
||||
{
|
||||
bro_analyzer()->ProtocolViolation(fmt("Invalid headers in SSL connection. Head1: %d, head2: %d, head3: %d", head1, head2, head3));
|
||||
return UNKNOWN_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 version = (head1<<8) | head2;
|
||||
if ( version != SSLv30 && version != TLSv10 &&
|
||||
version != TLSv11 && version != TLSv12 )
|
||||
{
|
||||
bro_analyzer()->ProtocolViolation(fmt("Invalid version in TLS connection. Version: %d", version));
|
||||
return UNKNOWN_VERSION;
|
||||
}
|
||||
|
||||
if ( head0 >=20 && head0 <= 30 )
|
||||
{ // ok, set record layer version, this never can be downgraded to v2
|
||||
record_layer_version_ = version;
|
||||
return version;
|
||||
}
|
||||
|
||||
bro_analyzer()->ProtocolViolation(fmt("Invalid type in TLS connection. Version: %d, Type: %d", version, head0));
|
||||
return UNKNOWN_VERSION;
|
||||
%}
|
||||
|
||||
function state() : int %{ return state_; %}
|
||||
function old_state() : int %{ return old_state_; %}
|
||||
function client_state() : int %{ return client_state_; %}
|
||||
|
||||
function transition(olds : AnalyzerState, news : AnalyzerState,
|
||||
current_record_is_orig : bool, is_orig : bool) : bool
|
||||
function server_state() : int %{ return client_state_; %}
|
||||
|
||||
function state(is_orig: bool) : int
|
||||
%{
|
||||
if ( (olds != STATE_ANY && olds != state_) ||
|
||||
current_record_is_orig != is_orig )
|
||||
return false;
|
||||
if ( is_orig )
|
||||
return client_state_;
|
||||
else
|
||||
return server_state_;
|
||||
%}
|
||||
|
||||
old_state_ = state_;
|
||||
state_ = news;
|
||||
|
||||
//printf("transitioning from %s to %s\n", state_label(old_state()).c_str(), state_label(state()).c_str());
|
||||
function startEncryption(is_orig: bool) : bool
|
||||
%{
|
||||
if ( is_orig )
|
||||
client_state_ = STATE_ENCRYPTED;
|
||||
else
|
||||
server_state_ = STATE_ENCRYPTED;
|
||||
return true;
|
||||
%}
|
||||
|
||||
function lost_track() : bool
|
||||
%{
|
||||
state_ = STATE_TRACK_LOST;
|
||||
return false;
|
||||
%}
|
||||
|
||||
function hello_requested() : bool
|
||||
%{
|
||||
bool ret = hello_requested_;
|
||||
hello_requested_ = false;
|
||||
return ret;
|
||||
%}
|
||||
|
||||
function set_hello_requested(val : bool) : bool
|
||||
%{
|
||||
hello_requested_ = val;
|
||||
return val;
|
||||
%}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue