QUIC: fix Initial dissection (draft -13)

Recognize the Token field in the Initial Packet. Accept Initial Packet
from server and adjust connection tracking logic to take this into
account.

Tested with ngtcp2-13.pcap, now the Initial packets can be decrypted
(even if the containing frames are not correctly handled).

Change-Id: I5937fb85172a2f93d1b4a4ed7667474ec04f95fc
Ping-Bug: 13881
Reviewed-on: https://code.wireshark.org/review/29641
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2018-09-13 00:34:46 +02:00 committed by Anders Broman
parent aedf4a7dd7
commit 63743a3733
1 changed files with 50 additions and 13 deletions

View File

@ -49,7 +49,9 @@ static int hf_quic_dcid = -1;
static int hf_quic_scid = -1;
static int hf_quic_dcil = -1;
static int hf_quic_scil = -1;
static int hf_quic_payload_length = -1;
static int hf_quic_token_length = -1;
static int hf_quic_token = -1;
static int hf_quic_length = -1;
static int hf_quic_packet_number = -1;
static int hf_quic_packet_number_full = -1;
static int hf_quic_version = -1;
@ -135,7 +137,7 @@ static dissector_handle_t ssl_handle;
* - During live capture, keys might not be immediately be available. 1-RTT
* client keys will be ready while client proceses Server Hello (Handshake).
* 1-RTT server keys will be ready while server creates Handshake message in
* response to Inititial Handshake.
* response to Initial Handshake.
* - So delay cipher creation until first short packet is received.
*
* Required input from TLS dissector: TLS-Exporter 0-RTT/1-RTT secrets and
@ -253,6 +255,7 @@ const value_string quic_version_vals[] = {
{ 0xff00000a, "draft-10" },
{ 0xff00000b, "draft-11" },
{ 0xff00000c, "draft-12" },
{ 0xff00000d, "draft-13" },
{ 0, NULL }
};
@ -682,6 +685,10 @@ quic_connection_find(packet_info *pinfo, guint8 long_packet_type,
if ((long_packet_type == QUIC_LPT_INITIAL || long_packet_type == QUIC_LPT_0RTT) && dcid->len > 0) {
conn = (quic_info_data_t *) wmem_map_lookup(quic_initial_connections, dcid);
// Both the client and server can send Initial (since draft -13).
if (!conn && long_packet_type == QUIC_LPT_INITIAL) {
conn = quic_connection_find_dcid(pinfo, dcid, from_server);
}
} else {
conn = quic_connection_find_dcid(pinfo, dcid, from_server);
}
@ -690,8 +697,8 @@ quic_connection_find(packet_info *pinfo, guint8 long_packet_type,
// For short packets, first try to find a match based on the address.
conn = quic_connection_find_dcid(pinfo, NULL, from_server);
if (conn) {
if ((from_server && !quic_cids_has_match(&conn->server_cids, dcid)) ||
(!from_server && !quic_cids_has_match(&conn->client_cids, dcid))) {
if ((*from_server && !quic_cids_has_match(&conn->server_cids, dcid)) ||
(!*from_server && !quic_cids_has_match(&conn->client_cids, dcid))) {
// Connection does not match packet.
conn = NULL;
}
@ -755,14 +762,15 @@ quic_connection_create_or_update(quic_info_data_t **conn_p,
switch (long_packet_type) {
case QUIC_LPT_INITIAL:
// The first Initial Packet creates a new connection.
if (!conn) {
// The first Initial Packet from the client creates a new connection.
if (!conn && !from_server) {
*conn_p = quic_connection_create(pinfo, version, scid, dcid);
}
break;
/* fallthrough */
case QUIC_LPT_RETRY:
case QUIC_LPT_HANDSHAKE:
// Remember CID from first server Retry/Handshake packet
// (or from the first server Initial packet, since draft -13).
if (conn && conn->server_cids.data.len == 0 && from_server) {
memcpy(&conn->server_cids.data, scid, sizeof(quic_cid_t));
if (scid->len) {
@ -1626,8 +1634,9 @@ dissect_quic_initial(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, g
// An Initial Packet should always result in creating a new connection.
DISSECTOR_ASSERT(quic_info);
quic_cipher *cipher = quic_packet->from_server ? &quic_info->server_handshake_cipher : &quic_info->client_handshake_cipher;
quic_process_payload(tvb, pinfo, quic_tree, ti, offset,
quic_info, quic_packet, &quic_info->client_handshake_cipher, pkn_len);
quic_info, quic_packet, cipher, pkn_len);
offset += tvb_reported_length_remaining(tvb, offset);
return offset;
@ -1758,6 +1767,8 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
guint32 long_packet_type;
guint32 version;
quic_cid_t dcid = {.len=0}, scid = {.len=0};
guint32 len_token_length;
guint64 token_length;
guint32 len_payload_length;
guint64 payload_length;
guint32 pkn_len;
@ -1771,15 +1782,26 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
offset = dissect_quic_long_header_common(tvb, pinfo, quic_tree, offset, quic_packet, &version, &dcid, &scid);
proto_tree_add_item_ret_varint(quic_tree, hf_quic_payload_length, tvb, offset, -1, ENC_VARINT_QUIC, &payload_length, &len_payload_length);
if (!is_quic_draft_max(version, 12) && long_packet_type == QUIC_LPT_INITIAL) {
proto_tree_add_item_ret_varint(quic_tree, hf_quic_token_length, tvb, offset, -1, ENC_VARINT_QUIC, &token_length, &len_token_length);
offset += len_token_length;
if (token_length) {
proto_tree_add_item(quic_tree, hf_quic_token, tvb, offset, (guint32)token_length, ENC_NA);
offset += (guint)token_length;
}
}
proto_tree_add_item_ret_varint(quic_tree, hf_quic_length, tvb, offset, -1, ENC_VARINT_QUIC, &payload_length, &len_payload_length);
offset += len_payload_length;
#ifdef HAVE_LIBGCRYPT_AEAD
/* Build handshake cipher now for PKN (and handshake) decryption. */
if (!PINFO_FD_VISITED(pinfo) && long_packet_type == QUIC_LPT_INITIAL) {
if (!PINFO_FD_VISITED(pinfo) && (long_packet_type == QUIC_LPT_INITIAL && !quic_packet->from_server) &&
conn && !memcmp(&dcid, &conn->client_dcid_initial, sizeof(quic_cid_t))) {
const gchar *error = NULL;
/* Create new decryption context based on the Client Connection
* ID from the Client Initial packet. */
* ID from the *very first* Client Initial packet. */
if (!quic_create_handshake_decoders(&dcid, &error, conn)) {
expert_add_info_format(pinfo, quic_tree, &ei_quic_decryption_failed, "Failed to create decryption context: %s", error);
quic_packet->decryption.error = wmem_strdup(wmem_file_scope(), error);
@ -1914,6 +1936,7 @@ dissect_quic_version_negotiation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *
static tvbuff_t *
quic_get_message_tvb(tvbuff_t *tvb, const guint offset)
{
guint64 token_length;
guint64 payload_length;
guint8 packet_type = tvb_get_guint8(tvb, offset);
if (packet_type & 0x80) {
@ -1932,6 +1955,10 @@ quic_get_message_tvb(tvbuff_t *tvb, const guint offset)
if (scil) {
length += 3 + scil;
}
if (!is_quic_draft_max(version, 12) && packet_type == (0x80 | QUIC_LPT_INITIAL)) {
length += tvb_get_varint(tvb, offset + length, 8, &token_length, ENC_VARINT_QUIC);
length += (guint)token_length;
}
length += tvb_get_varint(tvb, offset + length, 8, &payload_length, ENC_VARINT_QUIC);
// draft <= 11 does not include PKN length in Payload Length, add it.
if (quic_draft_version(version) == 11) {
@ -2176,11 +2203,21 @@ proto_register_quic(void)
FT_UINT8, BASE_DEC, VALS(quic_cid_len_vals), 0x0f,
"Source Connection ID Length (for non-zero lengths, add 3 for actual length)", HFILL }
},
{ &hf_quic_payload_length,
{ "Payload Length", "quic.payload_length",
{ &hf_quic_token_length,
{ "Token Length", "quic.token_length",
FT_UINT64, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_quic_token,
{ "Token", "quic.token",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_quic_length,
{ "Length", "quic.length",
FT_UINT64, BASE_DEC, NULL, 0x0,
"Length of Packet Number and Payload fields", HFILL }
},
{ &hf_quic_packet_number,
{ "Packet Number", "quic.packet_number",