diff options
author | Nardi Ivan <nardi.ivan@gmail.com> | 2020-09-04 14:11:34 +0200 |
---|---|---|
committer | AndersBroman <a.broman58@gmail.com> | 2020-09-05 07:02:04 +0000 |
commit | a46b62fcab53a42ce09764b03cccf4bd2136931d (patch) | |
tree | 5aeb4f0a3d6c13280f097326514c73d5d995b611 /epan | |
parent | 51cbb47e1ffef1de5949432a6a03519468a8ae3f (diff) |
(G)QUIC: improve dissection capabilities (Q050, T050 and T051)
Fix support for Q050 and add support for T050 and T051.
For these 3 versions, add dissection of (at least) Initial Packets.
For salts and other info, see:
"A Guide to Parsing QUIC Client Hellos for Network Middlebox Vendors"
https://docs.google.com/document/d/1GV2j-PGl7YGFqmWbYvzu7-UNVIpFdbprtmN9tt6USG8/preview
Note these versions are actively used by Chrome right now.
Based on https://code.wireshark.org/review/#/c/37492/ done by @alagoutte
Diffstat (limited to 'epan')
-rw-r--r-- | epan/dissectors/packet-gquic.c | 101 | ||||
-rw-r--r-- | epan/dissectors/packet-quic.c | 48 | ||||
-rw-r--r-- | epan/dissectors/packet-quic.h | 10 |
3 files changed, 134 insertions, 25 deletions
diff --git a/epan/dissectors/packet-gquic.c b/epan/dissectors/packet-gquic.c index 7ce7a86026..1764223df8 100644 --- a/epan/dissectors/packet-gquic.c +++ b/epan/dissectors/packet-gquic.c @@ -24,13 +24,14 @@ QUIC source code in Chromium : https://code.google.com/p/chromium/codesearch#chr #include <epan/expert.h> #include <epan/conversation.h> #include <epan/dissectors/packet-http2.h> +#include <epan/dissectors/packet-quic.h> #include <wsutil/strtoi.h> void proto_register_gquic(void); void proto_reg_handoff_gquic(void); static dissector_handle_t gquic_handle; - +static dissector_handle_t tls13_handshake_handle; static int proto_gquic = -1; static int hf_gquic_puflags = -1; @@ -71,6 +72,9 @@ static int hf_gquic_frame_type_wu_byte_offset = -1; static int hf_gquic_frame_type_blocked_stream_id = -1; static int hf_gquic_frame_type_sw_send_entropy = -1; static int hf_gquic_frame_type_sw_least_unacked_delta = -1; +static int hf_gquic_crypto_offset = -1; +static int hf_gquic_crypto_length = -1; +static int hf_gquic_crypto_crypto_data = -1; static int hf_gquic_frame_type_stream = -1; static int hf_gquic_frame_type_stream_f = -1; static int hf_gquic_frame_type_stream_d = -1; @@ -187,13 +191,6 @@ static expert_field ei_gquic_tag_unknown = EI_INIT; static expert_field ei_gquic_version_invalid = EI_INIT; -typedef struct gquic_info_data { - guint8 version; - gboolean version_valid; - gboolean encoding; - guint16 server_port; -} gquic_info_data_t; - #define GQUIC_MIN_LENGTH 3 #define GQUIC_MAGIC2 0x513032 #define GQUIC_MAGIC3 0x513033 @@ -252,6 +249,9 @@ static const value_string puflags_pkn_vals[] = { #define FT_BLOCKED 0x05 #define FT_STOP_WAITING 0x06 #define FT_PING 0x07 +/* CRYPTO is not a real GQUIC frame, but a QUIC one. Since some GQUIC flows + * have this kind of frame, try handling it like all the others */ +#define FT_CRYPTO 0x08 /**************************************************************************/ /* Frame Type Special */ @@ -280,7 +280,8 @@ static const range_string frame_type_vals[] = { { 5,5, "BLOCKED" }, { 6,6, "STOP_WAITING" }, { 7,7, "PING" }, - { 8,31, "Unknown" }, + { 8,8, "CRYPTO" }, + { 9,31, "Unknown" }, { 32,63, "CONGESTION_FEEDBACK (Special Frame Type)" }, { 64,127, "ACK (Special Frame Type)" }, { 128,256, "STREAM (Special Frame Type)" }, @@ -1196,7 +1197,6 @@ gboolean is_gquic_unencrypt(tvbuff_t *tvb, packet_info *pinfo, guint offset, gui break; } } else { - /* Special Frame Type */ if(frame_type & FTFLAGS_STREAM){ /* Stream */ @@ -1638,13 +1638,28 @@ dissect_gquic_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, gui } -static int +static guint32 +dissect_gquic_tags(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ft_tree, guint offset, gquic_info_data_t *gquic_info){ + guint32 tag_number; + + proto_tree_add_item(ft_tree, hf_gquic_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN); + tag_number = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN); + offset += 2; + + proto_tree_add_item(ft_tree, hf_gquic_padding, tvb, offset, 2, ENC_NA); + offset += 2; + + offset = dissect_gquic_tag(tvb, pinfo, ft_tree, offset, tag_number, gquic_info); + + return offset; +} + +int dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint8 len_pkn, gquic_info_data_t *gquic_info){ proto_item *ti, *ti_ft, *ti_ftflags /*, *expert_ti*/; proto_tree *ft_tree, *ftflags_tree; guint8 frame_type; guint8 num_ranges, num_revived, num_blocks = 0, num_timestamp; - guint32 tag_number; guint32 len_stream = 0, len_offset = 0, len_data = 0, len_largest_observed = 1, len_missing_packet = 1; ti_ft = proto_tree_add_item(gquic_tree, hf_gquic_frame, tvb, offset, 1, ENC_NA); @@ -1655,7 +1670,7 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr frame_type = tvb_get_guint8(tvb, offset); proto_item_set_text(ti_ft, "%s", rval_to_str(frame_type, frame_type_vals, "Unknown")); - if((frame_type & FTFLAGS_SPECIAL) == 0){ /* Regular Stream Flags */ + if((frame_type & FTFLAGS_SPECIAL) == 0 && frame_type != FT_CRYPTO){ /* Regular Stream Flags */ offset += 1; switch(frame_type){ case FT_PADDING:{ @@ -1665,7 +1680,8 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr ti_pad_len = proto_tree_add_uint(ft_tree, hf_gquic_frame_type_padding_length, tvb, offset, 0, pad_len); proto_item_set_generated(ti_pad_len); proto_item_append_text(ti_ft, " Length: %u", pad_len); - proto_tree_add_item(ft_tree, hf_gquic_frame_type_padding, tvb, offset, -1, ENC_NA); + if(pad_len > 0) /* Avoid Malformed Exception with pad_len == 0 */ + proto_tree_add_item(ft_tree, hf_gquic_frame_type_padding, tvb, offset, -1, ENC_NA); offset += pad_len; } break; @@ -1757,7 +1773,37 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr ftflags_tree = proto_item_add_subtree(ti_ftflags, ett_gquic_ftflags); proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream , tvb, offset, 1, ENC_NA); - if(frame_type & FTFLAGS_STREAM){ /* Stream Flags */ + if(frame_type == FT_CRYPTO) { + guint64 crypto_offset, crypto_length; + gint32 lenvar; + + DISSECTOR_ASSERT(gquic_info->version_valid && gquic_info->version >= 50); + + col_append_fstr(pinfo->cinfo, COL_INFO, ", CRYPTO"); + offset += 1; + proto_tree_add_item_ret_varint(ft_tree, hf_gquic_crypto_offset, tvb, offset, -1, ENC_VARINT_QUIC, &crypto_offset, &lenvar); + offset += lenvar; + proto_tree_add_item_ret_varint(ft_tree, hf_gquic_crypto_length, tvb, offset, -1, ENC_VARINT_QUIC, &crypto_length, &lenvar); + offset += lenvar; + proto_tree_add_item(ft_tree, hf_gquic_crypto_crypto_data, tvb, offset, (guint32)crypto_length, ENC_NA); + + if (gquic_info->version == 50) { + message_tag = tvb_get_ntohl(tvb, offset); + ti = proto_tree_add_item_ret_string(ft_tree, hf_gquic_tag, tvb, offset, 4, ENC_ASCII|ENC_NA, wmem_packet_scope(), &message_tag_str); + proto_item_append_text(ti, " (%s)", val_to_str(message_tag, message_tag_vals, "Unknown Tag")); + col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(message_tag, message_tag_vals, "Unknown")); + offset += 4; + + offset = dissect_gquic_tags(tvb, pinfo, ft_tree, offset, gquic_info); + } else { /* T050 and T051 */ + tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, (int)crypto_length); + col_set_writable(pinfo->cinfo, -1, FALSE); + call_dissector_with_data(tls13_handshake_handle, next_tvb, pinfo, ft_tree, GUINT_TO_POINTER(crypto_offset)); + col_set_writable(pinfo->cinfo, -1, TRUE); + offset += (guint32)crypto_length; + } + + } else if(frame_type & FTFLAGS_STREAM){ /* Stream Flags */ proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_f, tvb, offset, 1, ENC_NA); proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_d, tvb, offset, 1, ENC_NA); if(frame_type & FTFLAGS_STREAM_D){ @@ -1799,14 +1845,7 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(message_tag, message_tag_vals, "Unknown")); offset += 4; - proto_tree_add_item(ft_tree, hf_gquic_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN); - tag_number = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN); - offset += 2; - - proto_tree_add_item(ft_tree, hf_gquic_padding, tvb, offset, 2, ENC_NA); - offset += 2; - - offset = dissect_gquic_tag(tvb, pinfo, ft_tree, offset, tag_number, gquic_info); + offset = dissect_gquic_tags(tvb, pinfo, ft_tree, offset, gquic_info); break; } case 3: { /* Reserved H2 HEADERS (or PUSH_PROMISE..) */ @@ -2412,6 +2451,21 @@ proto_register_gquic(void) FT_UINT64, BASE_DEC, NULL, 0x0, "A variable length packet number delta with the same length as the packet header's packet number", HFILL } }, + { &hf_gquic_crypto_offset, + { "Offset", "gquic.crypto.offset", + FT_UINT64, BASE_DEC, NULL, 0x0, + "Byte offset into the stream", HFILL } + }, + { &hf_gquic_crypto_length, + { "Length", "gquic.crypto.length", + FT_UINT64, BASE_DEC, NULL, 0x0, + "Length of the Crypto Data field", HFILL } + }, + { &hf_gquic_crypto_crypto_data, + { "Crypto Data", "gquic.crypto.crypto_data", + FT_NONE, BASE_NONE, NULL, 0x0, + "The cryptographic message data", HFILL } + }, { &hf_gquic_frame_type_stream, { "Stream", "gquic.frame_type.stream", FT_BOOLEAN, 8, NULL, FTFLAGS_STREAM, @@ -2920,6 +2974,7 @@ proto_register_gquic(void) void proto_reg_handoff_gquic(void) { + tls13_handshake_handle = find_dissector("tls13-handshake"); dissector_add_uint_range_with_preference("udp.port", "", gquic_handle); heur_dissector_add("udp", dissect_gquic_heur, "Google QUIC", "gquic", proto_gquic, HEURISTIC_ENABLE); } diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c index 5110bd211c..cf369b0d19 100644 --- a/epan/dissectors/packet-quic.c +++ b/epan/dissectors/packet-quic.c @@ -326,6 +326,7 @@ typedef struct quic_info_data { dissector_handle_t app_handle; /**< Application protocol handle (NULL if unknown). */ wmem_map_t *client_streams; /**< Map from Stream ID -> STREAM info (guint64 -> quic_stream_state), sent by the client. */ wmem_map_t *server_streams; /**< Map from Stream ID -> STREAM info (guint64 -> quic_stream_state), sent by the server. */ + gquic_info_data_t *gquic_info; /**< GQUIC info for >Q050 flows. */ } quic_info_data_t; /** Per-packet information about QUIC, populated on the first pass. */ @@ -376,6 +377,13 @@ static inline guint8 quic_draft_version(guint32 version) { if (version == 0xfaceb002) { return 27; } + /* GQUIC Q050, T050 and T051: they are not really based on any drafts, + * but we must return a sensible value */ + if (version == 0x51303530 || + version == 0x54303530 || + version == 0x54303531) { + return 27; + } return 0; } @@ -387,7 +395,9 @@ static inline gboolean is_quic_draft_max(guint32 version, guint8 max_version) { const value_string quic_version_vals[] = { { 0x00000000, "Version Negotiation" }, { 0x51303434, "Google Q044" }, - { 0x51303530, "Google Q050 (draft-27)" }, + { 0x51303530, "Google Q050" }, + { 0x54303530, "Google T050" }, + { 0x54303531, "Google T051" }, { 0xfaceb001, "Facebook mvfst (draft-22)" }, { 0xfaceb002, "Facebook mvfst (draft-27)" }, { 0xff000004, "draft-04" }, @@ -894,6 +904,22 @@ quic_connection_create(packet_info *pinfo, guint32 version) conversation_t *conv = find_or_create_conversation(pinfo); conversation_add_proto_data(conv, proto_quic, conn); + if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531) { + gquic_info_data_t *gquic_info; + + gquic_info = wmem_new(wmem_file_scope(), gquic_info_data_t); + if (version == 0x51303530) + gquic_info->version = 50; + else if (version == 0x54303530) + gquic_info->version = 150; + else + gquic_info->version = 151; + gquic_info->encoding = ENC_LITTLE_ENDIAN; + gquic_info->version_valid = TRUE; + gquic_info->server_port = pinfo->destport; + conn->gquic_info = gquic_info; + } + return conn; } @@ -1985,6 +2011,14 @@ quic_derive_initial_secrets(const quic_cid_t *cid, 0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, 0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45 }; + static const guint8 hanshake_salt_draft_t50[20] = { + 0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, 0x91, 0x55, 0x80, + 0x30, 0x4c, 0x43, 0xa2, 0x36, 0x7c, 0x60, 0x48, 0x83, 0x10 + }; + static const gint8 hanshake_salt_draft_t51[20] = { + 0x7a, 0x4e, 0xde, 0xf4, 0xe7, 0xcc, 0xee, 0x5f, 0xa4, 0x50, + 0x6c, 0x19, 0x12, 0x4f, 0xc8, 0xcc, 0xda, 0x6e, 0x03, 0x3d + }; gcry_error_t err; guint8 secret[HASH_SHA2_256_LENGTH]; @@ -1992,6 +2026,12 @@ quic_derive_initial_secrets(const quic_cid_t *cid, if (version == 0x51303530) { err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_q50, sizeof(hanshake_salt_draft_q50), cid->cid, cid->len, secret); + } else if (version == 0x54303530) { + err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t50, sizeof(hanshake_salt_draft_t50), + cid->cid, cid->len, secret); + } else if (version == 0x54303531) { + err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t51, sizeof(hanshake_salt_draft_t51), + cid->cid, cid->len, secret); } else if (is_quic_draft_max(version, 22)) { err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_22, sizeof(handshake_salt_draft_22), cid->cid, cid->len, secret); @@ -2382,7 +2422,11 @@ quic_process_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_ guint decrypted_offset = 0; while (tvb_reported_length_remaining(decrypted_tvb, decrypted_offset) > 0) { - decrypted_offset = dissect_quic_frame_type(decrypted_tvb, pinfo, tree, decrypted_offset, quic_info, from_server); + if (quic_info->version == 0x51303530 || quic_info->version == 0x54303530 || quic_info->version == 0x54303531) { + decrypted_offset = dissect_gquic_frame_type(decrypted_tvb, pinfo, tree, decrypted_offset, pkn_len, quic_info->gquic_info); + } else { + decrypted_offset = dissect_quic_frame_type(decrypted_tvb, pinfo, tree, decrypted_offset, quic_info, from_server); + } } } else if (quic_info->skip_decryption) { expert_add_info_format(pinfo, ti, &ei_quic_decryption_failed, diff --git a/epan/dissectors/packet-quic.h b/epan/dissectors/packet-quic.h index 1802ec2329..5bb3074b58 100644 --- a/epan/dissectors/packet-quic.h +++ b/epan/dissectors/packet-quic.h @@ -50,6 +50,16 @@ void *quic_stream_get_proto_data(struct _packet_info *pinfo, quic_stream_info /** Returns the number of items for quic.connection.number. */ WS_DLL_PUBLIC guint32 get_quic_connections_count(void); +typedef struct gquic_info_data { + guint8 version; + gboolean version_valid; + gboolean encoding; + guint16 server_port; +} gquic_info_data_t; + +int +dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint8 len_pkn, gquic_info_data_t *gquic_info); + #ifdef __cplusplus } #endif /* __cplusplus */ |