From 7bf8a87429ee61526132650e27763ed203d880ce Mon Sep 17 00:00:00 2001 From: Ludovic Cintrat Date: Fri, 29 May 2020 10:21:12 +0200 Subject: [PATCH] DTLS: Add connection ID extension support * Add DTLS connection ID extension based on draft-ietf-tls-dtls-connection-id-07, excerpt: A CID is an identifier carried in the record layer header that gives the recipient additional information for selecting the appropriate security association. * Support session tracking based on connection ID, i.e. a connection ID list is built then looked up to retrieve the session of a packet, then the related conversation is updated with this session. Bug: 16600 Change-Id: I050d7b5b09dad33eb39d506aca67ee839b3b7181 Reviewed-on: https://code.wireshark.org/review/37351 Petri-Dish: Anders Broman Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- epan/dissectors/packet-dtls.c | 180 ++++++++++++++++++++++++++--- epan/dissectors/packet-tls-utils.c | 132 ++++++++++++++++++++- epan/dissectors/packet-tls-utils.h | 52 ++++++++- epan/dissectors/packet-tls.c | 8 +- 4 files changed, 342 insertions(+), 30 deletions(-) diff --git a/epan/dissectors/packet-dtls.c b/epan/dissectors/packet-dtls.c index bf359e2aca..97783a998e 100644 --- a/epan/dissectors/packet-dtls.c +++ b/epan/dissectors/packet-dtls.c @@ -90,11 +90,14 @@ static gint exported_pdu_tap = -1; static gint proto_dtls = -1; static gint hf_dtls_record = -1; static gint hf_dtls_record_content_type = -1; +static gint hf_dtls_record_special_type = -1; static gint hf_dtls_record_version = -1; static gint hf_dtls_record_epoch = -1; static gint hf_dtls_record_sequence_number = -1; +static gint hf_dtls_record_connection_id = -1; static gint hf_dtls_record_length = -1; static gint hf_dtls_record_appdata = -1; +static gint hf_dtls_record_encrypted_content = -1; static gint hf_dtls_alert_message = -1; static gint hf_dtls_alert_message_level = -1; static gint hf_dtls_alert_message_description = -1; @@ -146,6 +149,8 @@ static expert_field ei_dtls_handshake_fragment_length_zero = EI_INIT; static expert_field ei_dtls_handshake_fragment_past_end_msg = EI_INIT; static expert_field ei_dtls_msg_len_diff_fragment = EI_INIT; static expert_field ei_dtls_heartbeat_payload_length = EI_INIT; +static expert_field ei_dtls_cid_invalid_content_type = EI_INIT; +static expert_field ei_dtls_cid_invalid_enc_content = EI_INIT; #ifdef HAVE_LIBGNUTLS static GHashTable *dtls_key_hash = NULL; @@ -207,11 +212,15 @@ dtls_init(void) prefs_set_preference_obsolete(keys_list_pref); } } + + ssl_init_cid_list(); } static void dtls_cleanup(void) { + ssl_cleanup_cid_list(); + #ifdef HAVE_LIBGNUTLS if (key_list_stack != NULL) { wmem_destroy_stack(key_list_stack); @@ -363,8 +372,8 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_ proto_item *ti; proto_tree *dtls_tree; guint32 offset; - SslDecryptSession *ssl_session; - SslSession *session; + SslDecryptSession *ssl_session = NULL; + SslSession *session = NULL; gint is_from_server; guint8 curr_layer_num_ssl = pinfo->curr_layer_num; @@ -385,7 +394,29 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_ * in addition to conv_version */ conversation = find_or_create_conversation(pinfo); - ssl_session = ssl_get_session(conversation, dtls_handle); + + guint8 record_type = tvb_get_guint8(tvb, offset); + + /* try to get decrypt session from the connection ID only for the first pass, + * it should be available from the conversation in the second pass + */ + if (record_type == SSL_ID_TLS12_CID && !PINFO_FD_VISITED(pinfo)) { + // CID length is not embedded in the packet + ssl_session = ssl_get_session_by_cid(tvb, offset+11); + + if (ssl_session) { + // update conversation + conversation_add_proto_data(conversation, + dissector_handle_get_protocol_index(dtls_handle), + ssl_session); + } + } + + /* if session cannot be retrieved from connection ID, get or create it from conversation */ + if (ssl_session == NULL) { + ssl_session = ssl_get_session(conversation, dtls_handle); + } + session = &ssl_session->session; if (session->last_nontls_frame != 0 && @@ -550,9 +581,42 @@ dtls_is_null_cipher(guint cipher ) } } +static void +dtls_save_decrypted_record(packet_info *pinfo, gint record_id, guint8 content_type, guint8 curr_layer_num_ssl) +{ + const guchar *data = dtls_decrypted_data.data; + guint datalen = dtls_decrypted_data_avail; + + if (datalen == 0) { + return; + } + + if (content_type == SSL_ID_TLS12_CID) { + /* + * The actual data is followed by the content type and then zero or + * more padding. Scan backwards for content type, skipping padding. + */ + while (datalen > 0 && data[datalen - 1] == 0) { + datalen--; + } + ssl_debug_printf("%s found %d padding bytes\n", G_STRFUNC, dtls_decrypted_data_avail - datalen); + if (datalen == 0) { + ssl_debug_printf("%s there is no room for content type!\n", G_STRFUNC); + return; + } + content_type = data[--datalen]; + if (datalen == 0) { + return; + } + } + + ssl_add_record_info(proto_dtls, pinfo, data, datalen, record_id, NULL, (ContentType)content_type, curr_layer_num_ssl); +} + static gboolean decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, SslDecryptSession *ssl, - guint8 content_type, guint16 record_version, guint16 record_length, guint8 curr_layer_num_ssl) + guint8 content_type, guint16 record_version, guint16 record_length, guint8 curr_layer_num_ssl, + const guchar *cid, guint8 cid_length) { gboolean success; SslDecoder *decoder; @@ -601,7 +665,7 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, SslDecryp return FALSE; } success = ssl_decrypt_record(ssl, decoder, content_type, record_version, FALSE, - tvb_get_ptr(tvb, offset, record_length), record_length, + tvb_get_ptr(tvb, offset, record_length), record_length, cid, cid_length, &dtls_compressed_data, &dtls_decrypted_data, &dtls_decrypted_data_avail) == 0; } else if (dtls_is_null_cipher(ssl->session.cipher)) { @@ -613,13 +677,8 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, SslDecryp success = FALSE; } - if (success && dtls_decrypted_data_avail > 0) { - const guchar *data = dtls_decrypted_data.data; - guint datalen = dtls_decrypted_data_avail; - - ssl_add_record_info(proto_dtls, pinfo, data, datalen, - tvb_raw_offset(tvb)+offset, - NULL, (ContentType)content_type, curr_layer_num_ssl); + if (success) { + dtls_save_decrypted_record(pinfo, tvb_raw_offset(tvb)+offset, content_type, curr_layer_num_ssl); } return success; } @@ -669,20 +728,41 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, * uint16 length; * opaque fragment[TLSPlaintext.length]; * } DTLSPlaintext; + * + * + * draft-ietf-tls-dtls-connection-id-07: + * + * struct { + * ContentType special_type = tls12_cid; + * ProtocolVersion version; + * uint16 epoch; + * uint48 sequence_number; + * opaque cid[cid_length]; // New field + * uint16 length; + * opaque enc_content[DTLSCiphertext.length]; + * } DTLSCiphertext; + * */ + guint32 dtls_record_length; guint32 record_length; guint16 version; guint16 epoch; guint64 sequence_number; guint8 content_type; + guint content_type_offset; guint8 next_byte; proto_tree *ti; proto_tree *dtls_record_tree; - proto_item *length_pi; + proto_item *length_pi, *ct_pi; tvbuff_t *decrypted; SslRecordInfo *record = NULL; heur_dtbl_entry_t *hdtbl_entry; + guint8 cid[DTLS_MAX_CID_LENGTH]; + guint8 cid_length; + + /* Connection ID length to use if any */ + cid_length = is_from_server ? session->client_cid_len : session->server_cid_len; /* * Get the record layer fields of interest @@ -691,7 +771,15 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, version = tvb_get_ntohs(tvb, offset + 1); epoch = tvb_get_ntohs(tvb, offset + 3); sequence_number = tvb_get_ntoh48(tvb, offset + 5); - record_length = tvb_get_ntohs(tvb, offset + 11); + + if (content_type == SSL_ID_TLS12_CID && cid_length > 0) { + tvb_memcpy(tvb, cid, offset + 11, cid_length); + record_length = tvb_get_ntohs(tvb, offset + cid_length + 11); + dtls_record_length = 13 + cid_length + record_length; + } else { + record_length = tvb_get_ntohs(tvb, offset + 11); + dtls_record_length = 13 + record_length; + } if (!ssl_is_valid_content_type(content_type)) { @@ -702,7 +790,7 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, /* Set the protocol column */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "DTLS"); - return offset + 13 + record_length; + return offset + dtls_record_length; } if (ssl) { @@ -725,12 +813,18 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, /* add the record layer subtree header */ ti = proto_tree_add_item(tree, hf_dtls_record, tvb, - offset, 13 + record_length, ENC_NA); + offset, dtls_record_length, ENC_NA); dtls_record_tree = proto_item_add_subtree(ti, ett_dtls_record); /* show the one-byte content type */ - proto_tree_add_item(dtls_record_tree, hf_dtls_record_content_type, - tvb, offset, 1, ENC_BIG_ENDIAN); + if (content_type == SSL_ID_TLS12_CID) { + ct_pi = proto_tree_add_item(dtls_record_tree, hf_dtls_record_special_type, + tvb, offset, 1, ENC_BIG_ENDIAN); + } else { + ct_pi = proto_tree_add_item(dtls_record_tree, hf_dtls_record_content_type, + tvb, offset, 1, ENC_BIG_ENDIAN); + } + content_type_offset = offset; offset++; /* add the version */ @@ -746,6 +840,12 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree_add_uint64(dtls_record_tree, hf_dtls_record_sequence_number, tvb, offset, 6, sequence_number); offset += 6; + if (content_type == SSL_ID_TLS12_CID) { + /* add connection ID */ + proto_tree_add_item(dtls_record_tree, hf_dtls_record_connection_id, tvb, offset, cid_length, ENC_NA); + offset += cid_length; + } + /* add the length */ length_pi = proto_tree_add_uint(dtls_record_tree, hf_dtls_record_length, tvb, offset, 2, record_length); @@ -771,14 +871,35 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, /* try to decrypt record on the first pass, if possible. Store decrypted * record for later usage (without having to decrypt again). */ if (ssl) { - decrypt_dtls_record(tvb, pinfo, offset, ssl, content_type, version, record_length, curr_layer_num_ssl); + decrypt_dtls_record(tvb, pinfo, offset, ssl, content_type, version, record_length, curr_layer_num_ssl, cid, cid_length); } decrypted = ssl_get_record_info(tvb, proto_dtls, pinfo, tvb_raw_offset(tvb)+offset, curr_layer_num_ssl, &record); if (decrypted) { add_new_data_source(pinfo, decrypted, "Decrypted DTLS"); + + if (content_type == SSL_ID_TLS12_CID) { + content_type = record->type; + ti = proto_tree_add_uint(dtls_record_tree, hf_dtls_record_content_type, + tvb, content_type_offset, 1, record->type); + proto_item_set_generated(ti); + } } ssl_check_record_length(&dissect_dtls_hf, pinfo, (ContentType)content_type, record_length, length_pi, session->version, decrypted); + /* extract the real record from the connection ID record */ + if (content_type == SSL_ID_TLS12_CID) { + proto_item_set_text(dtls_record_tree, "%s Record Layer: Connection ID", + val_to_str_const(session->version, ssl_version_short_names, "DTLS")); + + /* if content cannot be deciphered or the content is invalid */ + if (decrypted == NULL) { + col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Connection ID"); + proto_tree_add_item(dtls_record_tree, hf_dtls_record_encrypted_content, tvb, + offset, record_length, ENC_NA); + offset += record_length; /* skip to end of record */ + return offset; + } + } switch ((ContentType) content_type) { case SSL_ID_CHG_CIPHER_SPEC: @@ -900,6 +1021,10 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo, session, record_length, FALSE); } break; + case SSL_ID_TLS12_CID: + expert_add_info_format(pinfo, ct_pi, &ei_dtls_cid_invalid_content_type, + "Invalid content type (%d)", content_type); + break; } offset += record_length; /* skip to end of record */ @@ -1741,6 +1866,11 @@ proto_register_dtls(void) FT_UINT8, BASE_DEC, VALS(ssl_31_content_type), 0x0, NULL, HFILL} }, + { &hf_dtls_record_special_type, + { "Special Type", "dtls.record.special_type", + FT_UINT8, BASE_DEC, VALS(ssl_31_content_type), 0x0, + "Always set to value 25, actual content type is known after decryption", HFILL} + }, { &hf_dtls_record_version, { "Version", "dtls.record.version", FT_UINT16, BASE_HEX, VALS(ssl_versions), 0x0, @@ -1756,6 +1886,11 @@ proto_register_dtls(void) FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_dtls_record_connection_id, + { "Connection ID", "dtls.record.connection_id", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, { &hf_dtls_record_length, { "Length", "dtls.record.length", FT_UINT16, BASE_DEC, NULL, 0x0, @@ -1766,6 +1901,11 @@ proto_register_dtls(void) FT_BYTES, BASE_NONE, NULL, 0x0, "Payload is encrypted application data", HFILL } }, + { &hf_dtls_record_encrypted_content, + { "Encrypted Record Content", "dtls.enc_content", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Encrypted record data", HFILL } + }, { & hf_dtls_alert_message, { "Alert Message", "dtls.alert_message", FT_NONE, BASE_NONE, NULL, 0x0, @@ -1923,6 +2063,8 @@ proto_register_dtls(void) { &ei_dtls_handshake_fragment_past_end_msg, { "dtls.handshake.fragment_past_end_msg", PI_PROTOCOL, PI_ERROR, "Fragment runs past the end of the message", EXPFILL }}, { &ei_dtls_msg_len_diff_fragment, { "dtls.msg_len_diff_fragment", PI_PROTOCOL, PI_ERROR, "Message length differs from value in earlier fragment", EXPFILL }}, { &ei_dtls_heartbeat_payload_length, { "dtls.heartbeat_message.payload_length.invalid", PI_MALFORMED, PI_ERROR, "Invalid heartbeat payload length", EXPFILL }}, + { &ei_dtls_cid_invalid_content_type, { "dtls.cid.content_type.invalid", PI_MALFORMED, PI_ERROR, "Invalid real content type", EXPFILL }}, + { &ei_dtls_cid_invalid_enc_content, { "dtls.cid.enc_content.invalid", PI_MALFORMED, PI_ERROR, "Invalid encrypted content", EXPFILL }}, SSL_COMMON_EI_LIST(dissect_dtls_hf, "dtls") }; diff --git a/epan/dissectors/packet-tls-utils.c b/epan/dissectors/packet-tls-utils.c index 1fb2d6382f..9cca5c82c8 100644 --- a/epan/dissectors/packet-tls-utils.c +++ b/epan/dissectors/packet-tls-utils.c @@ -507,6 +507,7 @@ const value_string ssl_31_content_type[] = { { 22, "Handshake" }, { 23, "Application Data" }, { 24, "Heartbeat" }, + { 25, "Connection ID" }, { 0x00, NULL } }; @@ -1166,6 +1167,7 @@ const value_string tls_hello_extension_types[] = { { SSL_HND_HELLO_EXT_POST_HANDSHAKE_AUTH, "post_handshake_auth" }, /* RFC 8446 */ { SSL_HND_HELLO_EXT_SIGNATURE_ALGORITHMS_CERT, "signature_algorithms_cert" }, /* RFC 8446 */ { SSL_HND_HELLO_EXT_KEY_SHARE, "key_share" }, /* RFC 8446 */ + { SSL_HND_HELLO_EXT_CONNECTION_ID, "connection_id" }, /* draft-ietf-tls-dtls-connection-id-07 */ { SSL_HND_HELLO_EXT_GREASE_0A0A, "Reserved (GREASE)" }, /* RFC 8701 */ { SSL_HND_HELLO_EXT_GREASE_1A1A, "Reserved (GREASE)" }, /* RFC 8701 */ { SSL_HND_HELLO_EXT_GREASE_2A2A, "Reserved (GREASE)" }, /* RFC 8701 */ @@ -1955,6 +1957,52 @@ gint ssl_get_keyex_alg(gint cipher) /* }}} */ } +static wmem_list_t *connection_id_session_list = NULL; + +void +ssl_init_cid_list(void) { + connection_id_session_list = wmem_list_new(wmem_file_scope()); +} + +void +ssl_cleanup_cid_list(void) { + wmem_destroy_list(connection_id_session_list); +} + +void +ssl_add_session_by_cid(SslDecryptSession *session) +{ + wmem_list_append(connection_id_session_list, session); +} + +SslDecryptSession * +ssl_get_session_by_cid(tvbuff_t *tvb, guint32 offset) +{ + SslDecryptSession * ssl_cid = NULL; + wmem_list_frame_t *it = wmem_list_head(connection_id_session_list); + + while (it != NULL && ssl_cid == NULL) { + SslDecryptSession * ssl = (SslDecryptSession *)wmem_list_frame_data(it); + DISSECTOR_ASSERT(ssl != NULL); + SslSession *session = &ssl->session; + + if (session->client_cid_len > 0 && tvb_bytes_exist(tvb, offset, session->client_cid_len)) { + if (tvb_memeql(tvb, offset, session->client_cid, session->client_cid_len) == 0) { + ssl_cid = ssl; + } + } + + if (session->server_cid_len > 0) { + if (tvb_memeql(tvb, offset, session->server_cid, session->server_cid_len) == 0) { + ssl_cid = ssl; + } + } + + it = wmem_list_frame_next(it); + } + + return ssl_cid; +} /* StringInfo structure (len + data) functions {{{ */ @@ -4069,7 +4117,9 @@ tls_decrypt_aead_record(SslDecryptSession *ssl, SslDecoder *decoder, _U_ #endif , - const guchar *in, guint16 inl, StringInfo *out_str, guint *outl) + const guchar *in, guint16 inl, + const guchar *cid, guint8 cidl, + StringInfo *out_str, guint *outl) { /* RFC 5246 (TLS 1.2) 6.2.3.3 defines the TLSCipherText.fragment as: * GenericAEADCipher: { nonce_explicit, [content] } @@ -4079,6 +4129,7 @@ tls_decrypt_aead_record(SslDecryptSession *ssl, SslDecoder *decoder, */ const guint16 version = ssl->session.version; const gboolean is_v12 = version == TLSV1DOT2_VERSION || version == DTLSV1DOT2_VERSION; + const gboolean is_cid = ct == SSL_ID_TLS12_CID && version == DTLSV1DOT2_VERSION; gcry_error_t err; const guchar *explicit_nonce = NULL, *ciphertext; guint ciphertext_len, auth_tag_len; @@ -4186,11 +4237,30 @@ tls_decrypt_aead_record(SslDecryptSession *ssl, SslDecoder *decoder, if (decoder->cipher_suite->mode == MODE_CCM || decoder->cipher_suite->mode == MODE_CCM_8) { /* size of plaintext, additional authenticated data and auth tag. */ guint64 lengths[3] = { ciphertext_len, is_v12 ? 13 : 0, auth_tag_len }; + if (is_cid) { + lengths[1] = 13 + 1 + cidl; /* cid length (1 byte) + cid (cidl bytes)*/ + } gcry_cipher_ctl(decoder->evp, GCRYCTL_SET_CCM_LENGTHS, lengths, sizeof(lengths)); } /* (D)TLS 1.2 needs specific AAD, TLS 1.3 (before -25) uses empty AAD. */ - if (is_v12) { + if (is_cid) { /* if connection ID */ + guchar aad[14+DTLS_MAX_CID_LENGTH]; + guint aad_len = 14 + cidl; + phton64(aad, decoder->seq); /* record sequence number */ + phton16(aad, decoder->epoch); /* DTLS 1.2 includes epoch. */ + aad[8] = ct; /* TLSCompressed.type */ + phton16(aad + 9, record_version); /* TLSCompressed.version */ + memcpy(aad + 11, cid, cidl); /* cid */ + aad[11 + cidl] = cidl; /* cid_length */ + phton16(aad + 12 + cidl, ciphertext_len); /* TLSCompressed.length */ + ssl_print_data("AAD", aad, aad_len); + err = gcry_cipher_authenticate(decoder->evp, aad, aad_len); + if (err) { + ssl_debug_printf("%s failed to set AAD: %s\n", G_STRFUNC, gcry_strerror(err)); + return FALSE; + } + } else if (is_v12) { guchar aad[13]; phton64(aad, decoder->seq); /* record sequence number */ if (version == DTLSV1DOT2_VERSION) { @@ -4272,10 +4342,11 @@ tls_decrypt_aead_record(SslDecryptSession *ssl, SslDecoder *decoder, /* Record decryption glue based on security parameters {{{ */ /* Assume that we are called only for a non-NULL decoder which also means that * we have a non-NULL decoder->cipher_suite. */ -int +gint ssl_decrypt_record(SslDecryptSession *ssl, SslDecoder *decoder, guint8 ct, guint16 record_version, gboolean ignore_mac_failed, - const guchar *in, guint16 inl, StringInfo *comp_str, StringInfo *out_str, guint *outl) + const guchar *in, guint16 inl, const guchar *cid, guint8 cidl, + StringInfo *comp_str, StringInfo *out_str, guint *outl) { guint pad, worklen, uncomplen, maclen, mac_fraglen = 0; guint8 *mac = NULL, *mac_frag = NULL; @@ -4304,7 +4375,7 @@ ssl_decrypt_record(SslDecryptSession *ssl, SslDecoder *decoder, guint8 ct, guint decoder->cipher_suite->mode == MODE_POLY1305 || ssl->session.version == TLSV1DOT3_VERSION) { - if (!tls_decrypt_aead_record(ssl, decoder, ct, record_version, ignore_mac_failed, in, inl, out_str, &worklen)) { + if (!tls_decrypt_aead_record(ssl, decoder, ct, record_version, ignore_mac_failed, in, inl, cid, cidl, out_str, &worklen)) { /* decryption failed */ return -1; } @@ -7563,6 +7634,53 @@ ssl_dissect_hnd_hello_ext_esni(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_i } /** TLS Extensions (in Client Hello and Server Hello). }}} */ +/* Connection ID dissection. {{{ */ +static guint32 +ssl_dissect_ext_connection_id(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, guint32 offset, SslDecryptSession *ssl, + guint8 cidl, guint8 **session_cid, guint8 *session_cidl) +{ + /* keep track of the decrypt session only for the first pass */ + if (cidl > 0 && !PINFO_FD_VISITED(pinfo)) { + tvb_ensure_bytes_exist(tvb, offset + 1, cidl); + *session_cidl = cidl; + *session_cid = (guint8*)wmem_alloc0(wmem_file_scope(), cidl); + tvb_memcpy(tvb, *session_cid, offset + 1, cidl); + if (ssl) { + ssl_add_session_by_cid(ssl); + } + } + + proto_tree_add_item(tree, hf->hf.hs_ext_connection_id_length, + tvb, offset, 1, ENC_NA); + offset++; + + proto_tree_add_item(tree, hf->hf.hs_ext_connection_id, + tvb, offset, cidl, ENC_NA); + offset += cidl; + + return offset; +} + +static guint32 +ssl_dissect_hnd_hello_ext_connection_id(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, guint32 offset, guint8 hnd_type, + SslSession *session, SslDecryptSession *ssl) +{ + guint8 cidl = tvb_get_guint8(tvb, offset); + + switch (hnd_type) { + case SSL_HND_CLIENT_HELLO: + return ssl_dissect_ext_connection_id(hf, tvb, pinfo, tree, offset, ssl, + cidl, &session->client_cid, &session->client_cid_len); + case SSL_HND_SERVER_HELLO: + return ssl_dissect_ext_connection_id(hf, tvb, pinfo, tree, offset, ssl, + cidl, &session->server_cid, &session->server_cid_len); + default: + return offset; + } +} /* }}} */ + /* Whether the Content and Handshake Types are valid; handle Protocol Version. {{{ */ gboolean ssl_is_valid_content_type(guint8 type) @@ -7573,6 +7691,7 @@ ssl_is_valid_content_type(guint8 type) case SSL_ID_HANDSHAKE: case SSL_ID_APP_DATA: case SSL_ID_HEARTBEAT: + case SSL_ID_TLS12_CID: return TRUE; } return FALSE; @@ -8758,6 +8877,9 @@ ssl_dissect_hnd_extension(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *t case SSL_HND_HELLO_EXT_ENCRYPTED_SERVER_NAME: offset = ssl_dissect_hnd_hello_ext_esni(hf, tvb, pinfo, ext_tree, offset, next_offset, hnd_type, ssl); break; + case SSL_HND_HELLO_EXT_CONNECTION_ID: + offset = ssl_dissect_hnd_hello_ext_connection_id(hf, tvb, pinfo, ext_tree, offset, hnd_type, session, ssl); + break; default: proto_tree_add_item(ext_tree, hf->hf.hs_ext_data, tvb, offset, ext_len, ENC_NA); diff --git a/epan/dissectors/packet-tls-utils.h b/epan/dissectors/packet-tls-utils.h index b2e9370692..54b2e7d828 100644 --- a/epan/dissectors/packet-tls-utils.h +++ b/epan/dissectors/packet-tls-utils.h @@ -39,7 +39,8 @@ typedef enum { SSL_ID_ALERT = 0x15, SSL_ID_HANDSHAKE = 0x16, SSL_ID_APP_DATA = 0x17, - SSL_ID_HEARTBEAT = 0x18 + SSL_ID_HEARTBEAT = 0x18, + SSL_ID_TLS12_CID = 0x19 } ContentType; typedef enum { @@ -121,6 +122,7 @@ typedef enum { #define SSL_HND_HELLO_EXT_POST_HANDSHAKE_AUTH 49 #define SSL_HND_HELLO_EXT_SIGNATURE_ALGORITHMS_CERT 50 #define SSL_HND_HELLO_EXT_KEY_SHARE 51 +#define SSL_HND_HELLO_EXT_CONNECTION_ID 53 #define SSL_HND_HELLO_EXT_GREASE_0A0A 2570 #define SSL_HND_HELLO_EXT_GREASE_1A1A 6682 #define SSL_HND_HELLO_EXT_GREASE_2A2A 10794 @@ -430,6 +432,19 @@ typedef struct _SslSession { /* First pass only: track an in-progress handshake reassembly (>0) */ guint32 client_hs_reassembly_id; guint32 server_hs_reassembly_id; + + /* Connection ID extension + + struct { + opaque cid<0..2^8-1>; + } ConnectionId; + */ +#define DTLS_MAX_CID_LENGTH 256 + + guint8 *client_cid; + guint8 *server_cid; + guint8 client_cid_len; + guint8 server_cid_len; } SslSession; /* RFC 5246, section 8.1 says that the master secret is always 48 bytes */ @@ -515,6 +530,22 @@ gboolean ssldecrypt_uat_fld_fileopen_chk_cb(void*, const char*, unsigned, const gboolean ssldecrypt_uat_fld_password_chk_cb(void*, const char*, unsigned, const void*, const void*, char** err); gchar* ssl_association_info(const char* dissector_table_name, const char* table_protocol); +/** Initialize the list of sessions with connection ID */ +void ssl_init_cid_list(void); + +/** Release resource allocated for the list of sessions with connection ID */ +void ssl_cleanup_cid_list(void); + +/** Add a session to the list of sessions using connection ID */ +void ssl_add_session_by_cid(SslDecryptSession *ssl); + +/** + * Return a session with a matching connection ID + * @param tvb a buffer containing a connection ID + * @param offset offset of the connection ID in tvb + */ +SslDecryptSession *ssl_get_session_by_cid(tvbuff_t *tvb, guint32 offset); + /** Retrieve a SslSession, creating it if it did not already exist. * @param conversation The SSL conversation. * @param tls_handle The dissector handle for SSL or DTLS. @@ -627,6 +658,8 @@ ssl_change_cipher(SslDecryptSession *ssl_session, gboolean server); @param ignore_mac_failed whether to ignore MAC or authenticity failures @param in a pointer to the ssl record to be decrypted @param inl the record length + @param cid a pointer to the connection ID to use in AEAD or NULL + @param cidl the connection ID length or 0 if cid is NULL @param comp_str a pointer to the store the compression data @param out_str a pointer to the store for the decrypted data @param outl the decrypted data len @@ -634,7 +667,8 @@ ssl_change_cipher(SslDecryptSession *ssl_session, gboolean server); extern gint ssl_decrypt_record(SslDecryptSession *ssl, SslDecoder *decoder, guint8 ct, guint16 record_version, gboolean ignore_mac_failed, - const guchar *in, guint16 inl, StringInfo *comp_str, StringInfo *out_str, guint *outl); + const guchar *in, guint16 inl, const guchar *cid, guint8 cidl, + StringInfo *comp_str, StringInfo *out_str, guint *outl); /** * Given a cipher algorithm and its mode, a hash algorithm and the secret (with @@ -801,6 +835,8 @@ typedef struct ssl_common_dissect { gint hs_ext_max_fragment_length; gint hs_ext_padding_data; gint hs_ext_type; + gint hs_ext_connection_id_length; + gint hs_ext_connection_id; gint hs_sig_hash_alg; gint hs_sig_hash_alg_len; gint hs_sig_hash_algs; @@ -1170,7 +1206,7 @@ ssl_common_dissect_t name = { \ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \ - -1, -1, -1, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, \ }, \ /* ett */ { \ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \ @@ -2146,6 +2182,16 @@ ssl_common_dissect_t name = { \ FT_UINT64, BASE_DEC, NULL, 0x00, \ NULL, HFILL } \ }, \ + { & name .hf.hs_ext_connection_id_length, \ + { "Connection ID length", prefix ".connection_id_length", \ + FT_UINT8, BASE_DEC, NULL, 0x00, \ + NULL, HFILL } \ + }, \ + { & name .hf.hs_ext_connection_id, \ + { "Connection ID", prefix ".connection_id", \ + FT_BYTES, BASE_NONE, NULL, 0x00, \ + NULL, HFILL } \ + }, \ { & name .hf.esni_suite, \ { "Cipher Suite", prefix ".esni.suite", \ FT_UINT16, BASE_HEX|BASE_EXT_STRING, &ssl_31_ciphersuite_ext, 0x0, \ diff --git a/epan/dissectors/packet-tls.c b/epan/dissectors/packet-tls.c index de5ffbf366..0d92bc0b73 100644 --- a/epan/dissectors/packet-tls.c +++ b/epan/dissectors/packet-tls.c @@ -1080,7 +1080,7 @@ decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, SslDecryp * is successful*/ ssl_decrypted_data_avail = ssl_decrypted_data.data_len; success = ssl_decrypt_record(ssl, decoder, content_type, record_version, tls_ignore_mac_failed, - tvb_get_ptr(tvb, offset, record_length), record_length, + tvb_get_ptr(tvb, offset, record_length), record_length, NULL, 0, &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0; /* */ if (!success) { @@ -1119,7 +1119,7 @@ decrypt_tls13_early_data(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, ssl_decrypted_data_avail = ssl_decrypted_data.data_len; success = ssl_decrypt_record(ssl, ssl->client, SSL_ID_APP_DATA, 0x303, FALSE, - tvb_get_ptr(tvb, offset, record_length), record_length, + tvb_get_ptr(tvb, offset, record_length), record_length, NULL, 0, &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0; if (success) { tls_save_decrypted_record(pinfo, tvb_raw_offset(tvb)+offset, ssl, SSL_ID_APP_DATA, ssl->client, TRUE, curr_layer_num_ssl); @@ -1157,7 +1157,7 @@ decrypt_tls13_early_data(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, } ssl_decrypted_data_avail = ssl_decrypted_data.data_len; - success = ssl_decrypt_record(ssl, ssl->client, SSL_ID_APP_DATA, 0x303, FALSE, record, record_length, + success = ssl_decrypt_record(ssl, ssl->client, SSL_ID_APP_DATA, 0x303, FALSE, record, record_length, NULL, 0, &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0; if (success) { ssl_debug_printf("Early data decryption succeeded, cipher = %#x\n", cipher); @@ -2077,6 +2077,8 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, dissect_ssl3_heartbeat(tvb, pinfo, ssl_record_tree, offset, session, record_length, plaintext); } break; + case SSL_ID_TLS12_CID: + break; } offset += record_length; /* skip to end of record */