mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Spicy TLS - refactoring and partial connection fix
The analyzer now detects partial connections at the beginning of a connection - and will skip them. This makes behavior more similar to the binpac analyzer. The decryption test is skipped. And some minor refacoring.
This commit is contained in:
parent
f95f5d2adb
commit
7c0c48b290
3 changed files with 97 additions and 44 deletions
|
@ -6,6 +6,8 @@ public function get_certificate_fuid(is_client: bool, pos: uint32): string &cxxn
|
||||||
|
|
||||||
public function get_ocsp_fuid(): string &cxxname="ssl_get_ocsp_fuid";
|
public function get_ocsp_fuid(): string &cxxname="ssl_get_ocsp_fuid";
|
||||||
|
|
||||||
|
public function is_partial_tcp(): bool &cxxname="ssl_is_partial_tcp";
|
||||||
|
|
||||||
type ContentType = enum {
|
type ContentType = enum {
|
||||||
change_cipher_spec = 20,
|
change_cipher_spec = 20,
|
||||||
alert = 21,
|
alert = 21,
|
||||||
|
@ -549,9 +551,9 @@ type Share = unit {
|
||||||
var flipped: bool;
|
var flipped: bool;
|
||||||
var flip_already_alerted: bool;
|
var flip_already_alerted: bool;
|
||||||
# version as seen in server_hello (for signature and hash-alg choice)
|
# version as seen in server_hello (for signature and hash-alg choice)
|
||||||
var chosen_version_sh: uint16;
|
var chosen_version_sh_outer: uint16;
|
||||||
# parsed version, can be used to distinguished tls 1.3
|
# final negotiated version - can e.g. be used to distinguished tls 1.3
|
||||||
var parsed_version: uint16;
|
var negotiated_version: uint16;
|
||||||
# set to true if chosen version is identified as a tls 1.3 version
|
# set to true if chosen version is identified as a tls 1.3 version
|
||||||
var tls_13: bool;
|
var tls_13: bool;
|
||||||
var chosen_cipher: uint16;
|
var chosen_cipher: uint16;
|
||||||
|
@ -569,7 +571,7 @@ type Share = unit {
|
||||||
self.ccs_seen = 0;
|
self.ccs_seen = 0;
|
||||||
self.invalid_dtls_version_count = 0;
|
self.invalid_dtls_version_count = 0;
|
||||||
self.tls_13 = False;
|
self.tls_13 = False;
|
||||||
self.parsed_version = UNKNOWN_VERSION;
|
self.negotiated_version = UNKNOWN_VERSION;
|
||||||
self.flipped = False;
|
self.flipped = False;
|
||||||
self.flip_already_alerted = False;
|
self.flip_already_alerted = False;
|
||||||
self.server_encrypted = False;
|
self.server_encrypted = False;
|
||||||
|
@ -619,13 +621,23 @@ function startEncryption(handshakesink: sink&, alertsink: sink&, inout sh: Share
|
||||||
# the server hello - and then again due to the supported_versions
|
# the server hello - and then again due to the supported_versions
|
||||||
# field in the server hello.
|
# field in the server hello.
|
||||||
function set_version(version: uint16, inout sh: Share): bool {
|
function set_version(version: uint16, inout sh: Share): bool {
|
||||||
sh.parsed_version = version;
|
sh.negotiated_version = version;
|
||||||
if (version == TLSv13 || version / 0xFF == 0x7F)
|
if (version == TLSv13 || version / 0xFF == 0x7F)
|
||||||
sh.tls_13 = True;
|
sh.tls_13 = True;
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# check for partial connection - and at the moment just disable the analyzer
|
||||||
|
# in that case, for 1:1 equivalence with binpac.
|
||||||
|
function check_partial(): bool {
|
||||||
|
if ( is_partial_tcp() ) {
|
||||||
|
zeek::skip_input();
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
# public type TLSMessage = unit {
|
# public type TLSMessage = unit {
|
||||||
# %context = Share;
|
# %context = Share;
|
||||||
#
|
#
|
||||||
|
@ -645,15 +657,18 @@ public type Message = unit {
|
||||||
sink alertsink;
|
sink alertsink;
|
||||||
var record_version: uint16;
|
var record_version: uint16;
|
||||||
var dtls: bool = False;
|
var dtls: bool = False;
|
||||||
|
var partial: bool = False;
|
||||||
|
|
||||||
on %init {
|
on %init {
|
||||||
self.handshakesink.connect(new Handshake(self, self.context()));
|
self.handshakesink.connect(new Handshake(self, self.context()));
|
||||||
self.alertsink.connect(new Alert(self.context()));
|
self.alertsink.connect(new Alert(self.context()));
|
||||||
# print "top-level init";
|
|
||||||
|
self.partial = check_partial();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
: skip bytes &eod if ( self.partial );
|
||||||
fragment: RecordFragmentChoice(self.handshakesink, self.alertsink, self, self.context())[];
|
fragment: RecordFragmentChoice(self.handshakesink, self.alertsink, self, self.context())[];
|
||||||
# : bytes &eod if ( self.context().skipping );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function is_dtls_version(version: uint16): bool {
|
function is_dtls_version(version: uint16): bool {
|
||||||
|
@ -672,9 +687,6 @@ type RecordFragmentChoice = unit(handshakesink: sink&, alertsink: sink&, inout m
|
||||||
False -> tlsfragment: TLSRecordFragment(self.content_type, handshakesink, alertsink, msg, sh);
|
False -> tlsfragment: TLSRecordFragment(self.content_type, handshakesink, alertsink, msg, sh);
|
||||||
};
|
};
|
||||||
|
|
||||||
# on content_type {
|
|
||||||
# print "Content type", self.content_type;
|
|
||||||
# }
|
|
||||||
on version {
|
on version {
|
||||||
msg.record_version = self.version;
|
msg.record_version = self.version;
|
||||||
if (is_dtls_version(self.version)) {
|
if (is_dtls_version(self.version)) {
|
||||||
|
@ -764,6 +776,10 @@ type PlaintextRecord = unit(content_type: uint8, handshakesink: sink&, alertsink
|
||||||
## a bit of context here - we can't really say when we get the first packet
|
## a bit of context here - we can't really say when we get the first packet
|
||||||
## that uses the final cryptographic key material - and will contain content
|
## that uses the final cryptographic key material - and will contain content
|
||||||
## data. We just don't have that information available in TLS 1.3 anymore.
|
## data. We just don't have that information available in TLS 1.3 anymore.
|
||||||
|
##
|
||||||
|
## Currently this returns True if the connection is encrypted, or in an otherwise
|
||||||
|
## unclear state (e.g. no known version for the connection), and False if the
|
||||||
|
## packet should be in plaintext
|
||||||
function determine_encryption_on(pr: PlaintextRecord, content_type: uint8, handshakesink: sink&, alertsink: sink&, inout sh: Share): bool {
|
function determine_encryption_on(pr: PlaintextRecord, content_type: uint8, handshakesink: sink&, alertsink: sink&, inout sh: Share): bool {
|
||||||
if (get_encrypted(sh))
|
if (get_encrypted(sh))
|
||||||
return True;
|
return True;
|
||||||
|
@ -831,16 +847,6 @@ type Handshake_message = unit(inout msg: Message, inout sh: Share) {
|
||||||
* -> unhandled: skip bytes &size=self.length;
|
* -> unhandled: skip bytes &size=self.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
# on msg_type {
|
|
||||||
# print "Handshake message", self.msg_type;
|
|
||||||
# }
|
|
||||||
# on unhandled {
|
|
||||||
# print "Unhandled handshake message of type ", self.msg_type;
|
|
||||||
# }
|
|
||||||
# on %error(emsg: string) {
|
|
||||||
# print "Error in handshake message of type", self.msg_type, self, emsg;
|
|
||||||
# print self;
|
|
||||||
# }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type HelloRequest = unit(inout sh: Share) {
|
type HelloRequest = unit(inout sh: Share) {
|
||||||
|
@ -915,10 +921,6 @@ type ClientHello = unit(len: uint64, msg: Message, inout sh: Share) {
|
||||||
compression_methods: uint8[self.compression_methods_length];
|
compression_methods: uint8[self.compression_methods_length];
|
||||||
extensions_length: uint16 if(len > self.offset() + 2);
|
extensions_length: uint16 if(len > self.offset() + 2);
|
||||||
extensions: Extension(sh, True)[] &size=self.extensions_length if(len > self.offset() + 2);
|
extensions: Extension(sh, True)[] &size=self.extensions_length if(len > self.offset() + 2);
|
||||||
# on %error(emsg: string) {
|
|
||||||
# print "Error in client hello", emsg;
|
|
||||||
# print self;
|
|
||||||
# }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Draft versions of TLS 1.3 had a different server hello - distinguish here
|
# Draft versions of TLS 1.3 had a different server hello - distinguish here
|
||||||
|
@ -929,9 +931,9 @@ type ServerHelloChoice = unit(len: uint64, msg: Message, inout sh: Share) {
|
||||||
server_version1: 8..15;
|
server_version1: 8..15;
|
||||||
server_version: 0..15;
|
server_version: 0..15;
|
||||||
};
|
};
|
||||||
var parsed_version: uint16;
|
var negotiated_version: uint16;
|
||||||
|
|
||||||
switch (self.parsed_version) {
|
switch (self.negotiated_version) {
|
||||||
TLSv13,
|
TLSv13,
|
||||||
TLSv13_draft,
|
TLSv13_draft,
|
||||||
0x7F00 -> sh_one_three: ServerHelloOneThree(len, msg, sh, self.sv.server_version);
|
0x7F00 -> sh_one_three: ServerHelloOneThree(len, msg, sh, self.sv.server_version);
|
||||||
|
@ -940,16 +942,16 @@ type ServerHelloChoice = unit(len: uint64, msg: Message, inout sh: Share) {
|
||||||
|
|
||||||
on sv {
|
on sv {
|
||||||
# print "Got server version", self.sv.server_version0, self.sv.server_version1, self.sv.server_version;
|
# print "Got server version", self.sv.server_version0, self.sv.server_version1, self.sv.server_version;
|
||||||
sh.chosen_version_sh = self.sv.server_version;
|
sh.chosen_version_sh_outer = self.sv.server_version;
|
||||||
set_version(self.sv.server_version, sh);
|
set_version(self.sv.server_version, sh);
|
||||||
# print "set chosen version", self.sv.server_version, sh.chosen_version_sh;
|
# print "set chosen version", self.sv.server_version, sh.chosen_version_sh_outer;
|
||||||
|
|
||||||
if (self.sv.server_version1 == 0x7F) {
|
if (self.sv.server_version1 == 0x7F) {
|
||||||
# print "TLS 13 draft";
|
# print "TLS 13 draft";
|
||||||
# map any draft version to draft 00
|
# map any draft version to draft 00
|
||||||
self.parsed_version = 0x7F00;
|
self.negotiated_version = 0x7F00;
|
||||||
} else {
|
} else {
|
||||||
self.parsed_version = self.sv.server_version;
|
self.negotiated_version = self.sv.server_version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -982,10 +984,6 @@ type ServerHello = unit(len: uint64, msg: Message, inout sh: Share, server_versi
|
||||||
sh.chosen_cipher = self.cipher_suite;
|
sh.chosen_cipher = self.cipher_suite;
|
||||||
# print "set chosen cipher", self.cipher_suite, sh.chosen_cipher;
|
# print "set chosen cipher", self.cipher_suite, sh.chosen_cipher;
|
||||||
}
|
}
|
||||||
# on %error(emsg: string) {
|
|
||||||
# print "Error in server hello", emsg;
|
|
||||||
# print self;
|
|
||||||
# }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Extension = unit(inout sh: Share, client_hello: bool) {
|
type Extension = unit(inout sh: Share, client_hello: bool) {
|
||||||
|
@ -1015,15 +1013,6 @@ type Extension = unit(inout sh: Share, client_hello: bool) {
|
||||||
* -> unknown: bytes &size=self.length;
|
* -> unknown: bytes &size=self.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
# on code {
|
|
||||||
# print "Extension", self.code, client_hello;
|
|
||||||
# }
|
|
||||||
# on unknown {
|
|
||||||
# print "Unknown extension", self.code;
|
|
||||||
# }
|
|
||||||
# on %error(emsg: string) {
|
|
||||||
# print "Error parsing extension with code", self.code, emsg;
|
|
||||||
# }
|
|
||||||
on raw {
|
on raw {
|
||||||
self.set_input(self.input() + 4);
|
self.set_input(self.input() + 4);
|
||||||
}
|
}
|
||||||
|
@ -1422,7 +1411,7 @@ type EcdheServerKeyExchange = unit(len: uint64, sh: Share) {
|
||||||
|
|
||||||
public function uses_signature_and_hashalgorithm(sh: Share): bool {
|
public function uses_signature_and_hashalgorithm(sh: Share): bool {
|
||||||
# larger TLS11 and not DTLSv10
|
# larger TLS11 and not DTLSv10
|
||||||
if (sh.chosen_version_sh > TLSv11 && sh.chosen_version_sh != DTLSv10)
|
if (sh.chosen_version_sh_outer > TLSv11 && sh.chosen_version_sh_outer != DTLSv10)
|
||||||
return True;
|
return True;
|
||||||
|
|
||||||
return False;
|
return False;
|
||||||
|
@ -1873,3 +1862,48 @@ on SSL::Certificate::%done {
|
||||||
first = False;
|
first = False;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Debug stuff
|
||||||
|
|
||||||
|
# on RecordFragmentChoice::content_type {
|
||||||
|
# print "RecordFragmentChoice: Content type", self.content_type;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on PlaintextRecord::unhandled {
|
||||||
|
# print "PlaintextRecord: Unhandled content type", content_type;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on Handshake_message::msg_type {
|
||||||
|
# print "Handshake message", self.msg_type;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on Handshake_message::unhandled {
|
||||||
|
# print "Unhandled handshake message of type ", self.msg_type;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on Handshake_message::%error(emsg: string) {
|
||||||
|
# print "Error in handshake message of type", self.msg_type, self, emsg;
|
||||||
|
# print self;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on ClientHello::%error(emsg: string) {
|
||||||
|
# print "Error in client hello", emsg;
|
||||||
|
# print self;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on ServerHello::%error(emsg: string) {
|
||||||
|
# print "Error in server hello", emsg;
|
||||||
|
# print self;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on Extension::code {
|
||||||
|
# print "Extension", self.code, client_hello;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on Extension::unknown {
|
||||||
|
# print "Unknown extension", self.code;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# on Extension::%error(emsg: string) {
|
||||||
|
# print "Error parsing extension with code", self.code, emsg;
|
||||||
|
# }
|
||||||
|
|
|
@ -43,3 +43,21 @@ std::string ssl_get_ocsp_fuid() {
|
||||||
std::string file_id = zeek::file_mgr->HashHandle(file_handle.Description());
|
std::string file_id = zeek::file_mgr->HashHandle(file_handle.Description());
|
||||||
return file_id;
|
return file_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ssl_is_partial_tcp() {
|
||||||
|
auto cookie = static_cast<zeek::spicy::rt::Cookie*>(hilti::rt::context::cookie());
|
||||||
|
assert(cookie);
|
||||||
|
|
||||||
|
auto x = cookie->protocol;
|
||||||
|
if ( ! x || ! x->analyzer )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto* tcp = dynamic_cast<zeek::analyzer::tcp::TCP_ApplicationAnalyzer*>(x->analyzer);
|
||||||
|
if ( ! tcp )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( tcp->TCP() && tcp->TCP()->IsPartial() )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# @TEST-REQUIRES: grep -q "#define OPENSSL_HAVE_KDF_H" $BUILD/zeek-config.h
|
# @TEST-REQUIRES: grep -q "#define OPENSSL_HAVE_KDF_H" $BUILD/zeek-config.h
|
||||||
|
# @TEST-REQUIRES: ! grep -q "#define ENABLE_SPICY_SSL" $BUILD/zeek-config.h
|
||||||
|
|
||||||
# @TEST-EXEC: zeek -B dpd -C -r $TRACES/tls/tls12-decryption.pcap %INPUT
|
# @TEST-EXEC: zeek -B dpd -C -r $TRACES/tls/tls12-decryption.pcap %INPUT
|
||||||
# @TEST-EXEC: btest-diff http.log
|
# @TEST-EXEC: btest-diff http.log
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue