diff --git a/scripts/base/protocols/ssl/consts.zeek b/scripts/base/protocols/ssl/consts.zeek index fb11ca22b5..7f7188859a 100644 --- a/scripts/base/protocols/ssl/consts.zeek +++ b/scripts/base/protocols/ssl/consts.zeek @@ -11,6 +11,7 @@ export { const DTLSv10 = 0xFEFF; # DTLSv11 does not exist const DTLSv12 = 0xFEFD; + const DTLSv13 = 0xFEFC; ## Mapping between the constants and string values for SSL/TLS versions. const version_strings: table[count] of string = { @@ -21,7 +22,8 @@ export { [TLSv12] = "TLSv12", [TLSv13] = "TLSv13", [DTLSv10] = "DTLSv10", - [DTLSv12] = "DTLSv12" + [DTLSv12] = "DTLSv12", + [DTLSv13] = "DTLSv13" } &default=function(i: count):string { if ( i/0xFF == 0x7F ) # TLS 1.3 draft diff --git a/src/analyzer/protocol/ssl/dtls-analyzer.pac b/src/analyzer/protocol/ssl/dtls-analyzer.pac index 1a68e0d846..61c1e21846 100644 --- a/src/analyzer/protocol/ssl/dtls-analyzer.pac +++ b/src/analyzer/protocol/ssl/dtls-analyzer.pac @@ -138,6 +138,31 @@ refine connection SSL_Conn += { return true; %} + + function proc_unified_record(is_orig: bool, ur: UnifiedRecord) : bool + %{ + // we don't have a CCS packet anymore - so let's just assume the connection is established once we have seen a packet from each direction. + if ( is_orig ) + client_state_ = STATE_ENCRYPTED; + else + server_state_ = STATE_ENCRYPTED; + + if ( client_state_ == STATE_ENCRYPTED && server_state_ == STATE_ENCRYPTED && established_ == false ) + { + established_ = true; + if ( ssl_established ) + zeek::BifEvent::enqueue_ssl_established(zeek_analyzer(), zeek_analyzer()->Conn()); + } + + if ( ssl_encrypted_data ) + { + // FIXME: swallow is not quite the correct length, because we are not parsing the entire header + zeek::BifEvent::enqueue_ssl_encrypted_data(zeek_analyzer(), + zeek_analyzer()->Conn(), is_orig ^ zeek_analyzer()->GetFlipped(), DTLSv13, APPLICATION_DATA, ur->swallow().length()); + } + + return true; + %} }; refine typeattr SSLRecord += &let { @@ -147,3 +172,7 @@ refine typeattr SSLRecord += &let { refine typeattr Handshake += &let { proc: bool = $context.connection.proc_handshake(rec, this); }; + +refine typeattr UnifiedRecord += &let { + proc: bool = $context.connection.proc_unified_record(is_orig, this); +}; diff --git a/src/analyzer/protocol/ssl/dtls-protocol.pac b/src/analyzer/protocol/ssl/dtls-protocol.pac index 760a0bc4e4..84f1542362 100644 --- a/src/analyzer/protocol/ssl/dtls-protocol.pac +++ b/src/analyzer/protocol/ssl/dtls-protocol.pac @@ -4,11 +4,37 @@ ###################################################################### type DTLSPDU(is_orig: bool) = record { - records: SSLRecord(is_orig)[] &transient; + records: SSLRecordSwitch(is_orig)[] &transient; }; -type SSLRecord(is_orig: bool) = record { - content_type: uint8; +# This feels like (another) really dirty hack. DTLS 1.3 introduces a new way in which ciphertext records +# can be encoded, using a new unified header, which is completely different from the earlier DTLS headers. +# It only is used after the client & server hello - which essentially are the same as in DTLS 1.2 (including +# using the same record-layer versions - which is why `dtls_version_ok` underneath does not refer to DTLS 1.3) +# The DTLS 1.3 unified header is signaled by the first 3 bits of the first byte being set to `001`, but only +# after DTLS 1.3 has been negotiated. +type SSLRecordSwitch(is_orig: bool) = record { + firstbyte: uint8; + + cont: case $context.connection.choose_record_type(firstbyte) of { + false -> rec: SSLRecord(firstbyte, is_orig); + true -> unified: UnifiedRecord(firstbyte, is_orig); + }; +}; + +type UnifiedRecord(firstbyte: uint8, is_orig: bool) = record { + # sequence_number: bytestring &length=(sequence_number_length?2:1); + # lets just ignore eveything for now. We have very limited example + # data, and it is hard to parse the CID due to variable length. + swallow: bytestring &restofdata; +} &let { + with_cid: bool = ((firstbyte&0x10)==0x10); + sequence_number_length: bool = ((firstbyte&0x08)==0x08); + lengh_present: bool = ((firstbyte&0x04)==0x04); + epoch_low_bits: uint8 = (firstbyte&0x03); +}; + +type SSLRecord(content_type: uint8, is_orig: bool) = record { version: uint16; # the epoch signalizes that a changecipherspec message has been received. Hence, everything with # an epoch > 0 should be encrypted @@ -83,4 +109,11 @@ refine connection SSL_Conn += { } %} + function choose_record_type(firstbyte: uint8): bool + %{ + uint16_t negotiated_version = zeek_analyzer()->GetNegotiatedVersion(); + if ( negotiated_version == DTLSv13 && ( (firstbyte & 0x20) == 0x20 ) ) + return true; + return false; + %} }; diff --git a/src/analyzer/protocol/ssl/ssl-defs.pac b/src/analyzer/protocol/ssl/ssl-defs.pac index 072c7d5de6..796fc2ba5a 100644 --- a/src/analyzer/protocol/ssl/ssl-defs.pac +++ b/src/analyzer/protocol/ssl/ssl-defs.pac @@ -77,6 +77,7 @@ function version_ok(vers : uint16) : bool case TLSv13: case DTLSv10: case DTLSv12: + case DTLSv13: return true; default: @@ -121,7 +122,8 @@ enum SSLVersions { DTLSv10 = 0xFEFF, # DTLSv11 does not exist. - DTLSv12 = 0xFEFD + DTLSv12 = 0xFEFD, + DTLSv13 = 0xFEFC }; enum SSLExtensions { diff --git a/testing/btest/Baseline/scripts.base.protocols.ssl.dtls-13/ssl.log b/testing/btest/Baseline/scripts.base.protocols.ssl.dtls-13/ssl.log new file mode 100644 index 0000000000..e8253d3f47 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ssl.dtls-13/ssl.log @@ -0,0 +1,11 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path ssl +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert +#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.168.36.147 33714 10.168.36.144 11111 DTLSv13 TLS_AES_128_GCM_SHA256 secp256r1 - F - - T CjCs - - - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Traces/tls/dtls13-wolfssl.pcap b/testing/btest/Traces/tls/dtls13-wolfssl.pcap new file mode 100644 index 0000000000..20b1b8ae96 Binary files /dev/null and b/testing/btest/Traces/tls/dtls13-wolfssl.pcap differ diff --git a/testing/btest/scripts/base/protocols/ssl/dtls-13.test b/testing/btest/scripts/base/protocols/ssl/dtls-13.test new file mode 100644 index 0000000000..61c358da21 --- /dev/null +++ b/testing/btest/scripts/base/protocols/ssl/dtls-13.test @@ -0,0 +1,6 @@ +# This tests a normal SSL connection and the log it outputs. + +# @TEST-EXEC: zeek -C -r $TRACES/tls/dtls13-wolfssl.pcap %INPUT +# @TEST-EXEC: btest-diff ssl.log +# @TEST-EXEC: test ! -f dpd.log +