/src/wireshark/epan/dissectors/packet-fbzero.c
Line | Count | Source |
1 | | /* packet-fbzero.c |
2 | | * Routines for Zero Protocol dissection |
3 | | * Copyright 2016-2017, Alexis La Goutte <alexis.lagoutte at gmail dot com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * Zero Protocol coming from Facebook and used the same framing like QUIC |
14 | | * (tag-map value) but based on TCP |
15 | | * |
16 | | * See |
17 | | * |
18 | | * https://engineering.fb.com/networking-traffic/building-zero-protocol-for-fast-secure-mobile-connections/ |
19 | | * |
20 | | * for some (not much!) information on it. |
21 | | * |
22 | | * It was reverse engineered based off of QUIC dissector functionality |
23 | | */ |
24 | | #include "config.h" |
25 | | |
26 | | #include <epan/packet.h> |
27 | | #include <epan/tfs.h> |
28 | | #include <epan/expert.h> |
29 | | #include <epan/conversation.h> |
30 | | |
31 | | void proto_register_fb_zero(void); |
32 | | void proto_reg_handoff_fb_zero(void); |
33 | | |
34 | | static int proto_fb_zero; |
35 | | |
36 | | static dissector_handle_t fb_zero_handle; |
37 | | |
38 | | static int hf_fb_zero_puflags; |
39 | | static int hf_fb_zero_puflags_vrsn; |
40 | | static int hf_fb_zero_puflags_unknown; |
41 | | static int hf_fb_zero_version; |
42 | | static int hf_fb_zero_length; |
43 | | static int hf_fb_zero_tag; |
44 | | static int hf_fb_zero_tags; |
45 | | static int hf_fb_zero_tag_number; |
46 | | static int hf_fb_zero_tag_value; |
47 | | static int hf_fb_zero_tag_type; |
48 | | static int hf_fb_zero_tag_offset_end; |
49 | | static int hf_fb_zero_tag_length; |
50 | | static int hf_fb_zero_tag_sni; |
51 | | static int hf_fb_zero_tag_vers; |
52 | | static int hf_fb_zero_tag_sno; |
53 | | static int hf_fb_zero_tag_aead; |
54 | | static int hf_fb_zero_tag_scid; |
55 | | static int hf_fb_zero_tag_time; |
56 | | static int hf_fb_zero_tag_alpn; |
57 | | static int hf_fb_zero_tag_pubs; |
58 | | static int hf_fb_zero_tag_kexs; |
59 | | static int hf_fb_zero_tag_nonc; |
60 | | |
61 | | static int hf_fb_zero_tag_unknown; |
62 | | |
63 | | static int hf_fb_zero_padding; |
64 | | static int hf_fb_zero_payload; |
65 | | static int hf_fb_zero_unknown; |
66 | | |
67 | | static int ett_fb_zero; |
68 | | static int ett_fb_zero_puflags; |
69 | | static int ett_fb_zero_prflags; |
70 | | static int ett_fb_zero_ft; |
71 | | static int ett_fb_zero_ftflags; |
72 | | static int ett_fb_zero_tag_value; |
73 | | |
74 | | static expert_field ei_fb_zero_tag_undecoded; |
75 | | static expert_field ei_fb_zero_tag_offset_end_invalid; |
76 | | static expert_field ei_fb_zero_length_invalid; |
77 | | |
78 | 0 | #define FBZERO_MIN_LENGTH 3 |
79 | | |
80 | 4.40k | #define VERSION_QTV 0x515456 |
81 | | |
82 | | /**************************************************************************/ |
83 | | /* Public Flags */ |
84 | | /**************************************************************************/ |
85 | 14 | #define PUFLAGS_VRSN 0x01 |
86 | 14 | #define PUFLAGS_UNKN 0xFE |
87 | | |
88 | | /**************************************************************************/ |
89 | | /* Message tag */ |
90 | | /**************************************************************************/ |
91 | | |
92 | 0 | #define MTAG_CHLO 0x43484C4F |
93 | 0 | #define MTAG_SNOM 0x534E4F4D |
94 | | #define MTAG_SHLO 0x53484C4F |
95 | | #define MTAG_REJ 0x52454A00 |
96 | | #define MTAG_PRST 0x50525354 |
97 | | |
98 | | static const value_string message_tag_vals[] = { |
99 | | { MTAG_CHLO, "Client Hello" }, |
100 | | { MTAG_SNOM, "Server NOM??" }, |
101 | | { MTAG_SHLO, "Server Hello" }, |
102 | | { MTAG_REJ, "Rejection" }, |
103 | | { MTAG_PRST, "Public Reset" }, |
104 | | { 0, NULL } |
105 | | }; |
106 | | |
107 | | /**************************************************************************/ |
108 | | /* Tag */ |
109 | | /**************************************************************************/ |
110 | | |
111 | 0 | #define TAG_SNI 0x534E4900 |
112 | 0 | #define TAG_VERS 0x56455253 |
113 | 0 | #define TAG_SNO 0x534E4F00 |
114 | 0 | #define TAG_AEAD 0x41454144 |
115 | 0 | #define TAG_SCID 0x53434944 |
116 | 0 | #define TAG_TIME 0x54494d45 |
117 | 0 | #define TAG_ALPN 0x414C504E |
118 | 0 | #define TAG_PUBS 0x50554253 |
119 | 0 | #define TAG_KEXS 0x4B455853 |
120 | 0 | #define TAG_NONC 0x4E4F4E43 |
121 | | |
122 | | static const value_string tag_vals[] = { |
123 | | { TAG_SNI, "Server Name Indication" }, |
124 | | { TAG_VERS, "Version" }, |
125 | | { TAG_SNO, "Server nonce" }, |
126 | | { TAG_AEAD, "Authenticated encryption algorithms" }, |
127 | | { TAG_SCID, "Server config ID" }, |
128 | | { TAG_TIME, "Time" }, |
129 | | { TAG_ALPN, "ALPN" }, |
130 | | { TAG_PUBS, "Public value" }, |
131 | | { TAG_KEXS, "Key exchange algorithms" }, |
132 | | { TAG_NONC, "Client Nonce" }, |
133 | | { 0, NULL } |
134 | | }; |
135 | | |
136 | | |
137 | | /**************************************************************************/ |
138 | | /* AEAD Tag */ |
139 | | /**************************************************************************/ |
140 | | |
141 | | #define AEAD_AESG 0x41455347 |
142 | | #define AEAD_S20P 0x53323050 |
143 | | #define AEAD_CC12 0x43433132 |
144 | | |
145 | | static const value_string tag_aead_vals[] = { |
146 | | { AEAD_AESG, "AES-GCM with a 12-byte tag and IV" }, |
147 | | { AEAD_S20P, "Salsa20 with Poly1305" }, |
148 | | { AEAD_CC12, "ChaCha12 with Poly1305" }, |
149 | | { 0, NULL } |
150 | | }; |
151 | | |
152 | | /**************************************************************************/ |
153 | | /* KEXS Tag */ |
154 | | /**************************************************************************/ |
155 | | |
156 | | #define KEXS_C255 0x43323535 |
157 | | #define KEXS_P256 0x50323536 |
158 | | |
159 | | static const value_string tag_kexs_vals[] = { |
160 | | { KEXS_C255, "Curve25519" }, |
161 | | { KEXS_P256, "P-256" }, |
162 | | { 0, NULL } |
163 | | }; |
164 | | |
165 | | |
166 | | static uint32_t |
167 | 0 | dissect_fb_zero_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *fb_zero_tree, unsigned offset, uint32_t tag_number){ |
168 | 0 | uint32_t tag_offset_start = offset + tag_number*4*2; |
169 | 0 | uint32_t tag_offset = 0, total_tag_len = 0; |
170 | 0 | int32_t tag_len = 0; |
171 | 0 | bool tag_offset_valid = true; |
172 | |
|
173 | 0 | while(tag_number){ |
174 | 0 | proto_tree *tag_tree; |
175 | 0 | proto_item *ti_tag, *ti_type, *ti_offset_len, *ti_len; |
176 | 0 | uint32_t offset_end, tag; |
177 | 0 | const uint8_t* tag_str; |
178 | | |
179 | | /* |
180 | | * This item covers the tag type and end offset; the tag values |
181 | | * are in a separate region following all the type/end offset |
182 | | * pairs. |
183 | | */ |
184 | 0 | ti_tag = proto_tree_add_item(fb_zero_tree, hf_fb_zero_tags, tvb, offset, 4*2, ENC_NA); |
185 | 0 | tag_tree = proto_item_add_subtree(ti_tag, ett_fb_zero_tag_value); |
186 | 0 | ti_type = proto_tree_add_item_ret_string(tag_tree, hf_fb_zero_tag_type, tvb, offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str); |
187 | 0 | tag = tvb_get_ntohl(tvb, offset); |
188 | 0 | proto_item_append_text(ti_type, " (%s)", val_to_str_const(tag, tag_vals, "Unknown")); |
189 | 0 | proto_item_append_text(ti_tag, ": %s (%s)", tag_str, val_to_str_const(tag, tag_vals, "Unknown")); |
190 | 0 | offset += 4; |
191 | |
|
192 | 0 | ti_offset_len = proto_tree_add_item_ret_uint(tag_tree, hf_fb_zero_tag_offset_end, tvb, offset, 4, ENC_LITTLE_ENDIAN, &offset_end); |
193 | 0 | if(offset_end < tag_offset){ |
194 | 0 | expert_add_info_format(pinfo, ti_offset_len, &ei_fb_zero_tag_offset_end_invalid, |
195 | 0 | "Invalid tag end offset %u < %u", |
196 | 0 | offset_end, tag_offset); |
197 | | /* |
198 | | * We can continue to show the tag type, end offset, and length, |
199 | | * but we can no longer show the values, or subsequent length |
200 | | * values, as the end offset in the tag value region is bogus. |
201 | | */ |
202 | 0 | tag_offset_valid = false; |
203 | 0 | } else { |
204 | 0 | tag_len = offset_end - tag_offset; |
205 | 0 | if(!tvb_bytes_exist(tvb, tag_offset_start + tag_offset, tag_len)){ |
206 | 0 | expert_add_info_format(pinfo, ti_offset_len, &ei_fb_zero_tag_offset_end_invalid, |
207 | 0 | "Invalid tag end offset %u past end of packet", |
208 | 0 | offset_end); |
209 | | /* |
210 | | * We can continue to show the tag type, end offset, |
211 | | * and length, but we can no longer show the values, |
212 | | * as the end offset in the tag value region is bogus. |
213 | | * |
214 | | * We don't just throw an exception here, because we |
215 | | * want to show all the tag type and end offset values. |
216 | | */ |
217 | 0 | tag_offset_valid = false; |
218 | 0 | } |
219 | 0 | total_tag_len += tag_len; |
220 | 0 | ti_len = proto_tree_add_uint(tag_tree, hf_fb_zero_tag_length, tvb, offset, 4, tag_len); |
221 | 0 | proto_item_append_text(ti_tag, " (l=%u)", tag_len); |
222 | 0 | proto_item_set_generated(ti_len); |
223 | 0 | } |
224 | 0 | offset += 4; |
225 | |
|
226 | 0 | if(tag_offset_valid){ |
227 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_value, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA); |
228 | 0 | } |
229 | |
|
230 | 0 | switch(tag){ |
231 | 0 | case TAG_SNI: |
232 | 0 | if(tag_offset_valid){ |
233 | 0 | proto_tree_add_item_ret_string(tag_tree, hf_fb_zero_tag_sni, tvb, tag_offset_start + tag_offset, tag_len, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str); |
234 | 0 | proto_item_append_text(ti_tag, ": %s", tag_str); |
235 | 0 | tag_offset += tag_len; |
236 | 0 | } |
237 | 0 | break; |
238 | 0 | case TAG_VERS: |
239 | 0 | if(tag_offset_valid){ |
240 | 0 | if(tag_len != 4){ |
241 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
242 | 0 | "Invalid tag length: %u, should be 4", tag_len); |
243 | 0 | tag_offset += tag_len; |
244 | 0 | break; |
245 | 0 | } |
246 | 0 | proto_tree_add_item_ret_string(tag_tree, hf_fb_zero_tag_vers, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str); |
247 | 0 | proto_item_append_text(ti_tag, ": %s", tag_str); |
248 | 0 | tag_offset += 4; |
249 | 0 | } |
250 | 0 | break; |
251 | 0 | case TAG_SNO: |
252 | 0 | if(tag_offset_valid){ |
253 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_sno, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA); |
254 | 0 | tag_offset += tag_len; |
255 | 0 | } |
256 | 0 | break; |
257 | 0 | case TAG_AEAD: |
258 | 0 | if(tag_offset_valid){ |
259 | 0 | while(offset_end - tag_offset >= 4){ |
260 | 0 | proto_tree *ti_aead; |
261 | 0 | ti_aead = proto_tree_add_item(tag_tree, hf_fb_zero_tag_aead, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII); |
262 | 0 | proto_item_append_text(ti_aead, " (%s)", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_aead_vals, "Unknown")); |
263 | 0 | proto_item_append_text(ti_tag, ", %s", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_aead_vals, "Unknown")); |
264 | 0 | tag_offset += 4; |
265 | 0 | } |
266 | 0 | if(offset_end - tag_offset > 0){ |
267 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
268 | 0 | "Invalid tag length: %u, should be a multiple of 4", tag_len); |
269 | 0 | tag_offset = offset_end; |
270 | 0 | } |
271 | 0 | } |
272 | 0 | break; |
273 | 0 | case TAG_SCID: |
274 | 0 | if(tag_offset_valid){ |
275 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_scid, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA); |
276 | 0 | tag_offset += tag_len; |
277 | 0 | } |
278 | 0 | break; |
279 | 0 | case TAG_TIME: |
280 | 0 | if(tag_offset_valid){ |
281 | 0 | if(tag_len != 4){ |
282 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
283 | 0 | "Invalid tag length: %u, should be 4", tag_len); |
284 | 0 | tag_offset += tag_len; |
285 | 0 | break; |
286 | 0 | } |
287 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_time, tvb, tag_offset_start + tag_offset, 4, ENC_LITTLE_ENDIAN); |
288 | 0 | proto_item_append_text(ti_tag, ": %u", tvb_get_letohl(tvb, tag_offset_start + tag_offset)); |
289 | 0 | tag_offset += 4; |
290 | 0 | } |
291 | 0 | break; |
292 | 0 | case TAG_ALPN: |
293 | 0 | if(tag_offset_valid){ |
294 | 0 | if(tag_len != 4){ |
295 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
296 | 0 | "Invalid tag length: %u, should be 4", tag_len); |
297 | 0 | tag_offset += tag_len; |
298 | 0 | break; |
299 | 0 | } |
300 | 0 | proto_tree_add_item_ret_string(tag_tree, hf_fb_zero_tag_alpn, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &tag_str); |
301 | 0 | proto_item_append_text(ti_tag, ": %s", tag_str); |
302 | 0 | tag_offset += 4; |
303 | 0 | } |
304 | 0 | break; |
305 | 0 | case TAG_PUBS: |
306 | 0 | if(tag_offset_valid){ |
307 | | /*TODO FIX: 24 Length + Pubs key?.. ! */ |
308 | 0 | if(tag_len < 2){ |
309 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
310 | 0 | "Invalid tag length: %u, should be >= 2", tag_len); |
311 | 0 | tag_offset += tag_len; |
312 | 0 | break; |
313 | 0 | } |
314 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_pubs, tvb, tag_offset_start + tag_offset, 2, ENC_LITTLE_ENDIAN); |
315 | 0 | tag_offset += 2; |
316 | 0 | while(offset_end - tag_offset >= 3){ |
317 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_pubs, tvb, tag_offset_start + tag_offset, 3, ENC_LITTLE_ENDIAN); |
318 | 0 | tag_offset += 3; |
319 | 0 | } |
320 | 0 | if(offset_end - tag_offset > 0){ |
321 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
322 | 0 | "Invalid tag length: %u, should be a multiple of 3", tag_len); |
323 | 0 | tag_offset = offset_end; |
324 | 0 | } |
325 | 0 | } |
326 | 0 | break; |
327 | 0 | case TAG_KEXS: |
328 | 0 | if(tag_offset_valid){ |
329 | 0 | while(offset_end - tag_offset >= 4){ |
330 | 0 | proto_tree *ti_kexs; |
331 | 0 | ti_kexs = proto_tree_add_item(tag_tree, hf_fb_zero_tag_kexs, tvb, tag_offset_start + tag_offset, 4, ENC_ASCII); |
332 | 0 | proto_item_append_text(ti_kexs, " (%s)", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_kexs_vals, "Unknown")); |
333 | 0 | proto_item_append_text(ti_tag, ", %s", val_to_str_const(tvb_get_ntohl(tvb, tag_offset_start + tag_offset), tag_kexs_vals, "Unknown")); |
334 | 0 | tag_offset += 4; |
335 | 0 | } |
336 | 0 | if(offset_end - tag_offset > 0){ |
337 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
338 | 0 | "Invalid tag length: %u, should be a multiple of 4", tag_len); |
339 | 0 | tag_offset = offset_end; |
340 | 0 | } |
341 | 0 | } |
342 | 0 | break; |
343 | 0 | case TAG_NONC: |
344 | 0 | if(tag_offset_valid){ |
345 | | /*TODO: Enhance display: 32 bytes consisting of 4 bytes of timestamp (big-endian, UNIX epoch seconds), 8 bytes of server orbit and 20 bytes of random data. */ |
346 | 0 | if(tag_len != 32){ |
347 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
348 | 0 | "Invalid tag length: %u, should be 32", tag_len); |
349 | 0 | tag_offset += tag_len; |
350 | 0 | break; |
351 | 0 | } |
352 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_nonc, tvb, tag_offset_start + tag_offset, 32, ENC_NA); |
353 | 0 | tag_offset += 32; |
354 | 0 | } |
355 | 0 | break; |
356 | 0 | default: |
357 | 0 | expert_add_info_format(pinfo, ti_tag, &ei_fb_zero_tag_undecoded, |
358 | 0 | "Dissector for FB Zero Tag" |
359 | 0 | " %s (%s) code not implemented, Contact" |
360 | 0 | " Wireshark developers if you want this supported", |
361 | 0 | tvb_get_string_enc(pinfo->pool, tvb, offset-8, 4, ENC_ASCII|ENC_NA), val_to_str_const(tag, tag_vals, "Unknown")); |
362 | 0 | if(tag_offset_valid){ |
363 | 0 | proto_tree_add_item(tag_tree, hf_fb_zero_tag_unknown, tvb, tag_offset_start + tag_offset, tag_len, ENC_NA); |
364 | 0 | tag_offset += tag_len; |
365 | 0 | } |
366 | 0 | break; |
367 | 0 | } |
368 | | |
369 | 0 | tag_number--; |
370 | 0 | } |
371 | | |
372 | | /* XXX - can this still occur? */ |
373 | 0 | if (offset + total_tag_len <= offset) { |
374 | 0 | expert_add_info_format(pinfo, fb_zero_tree, &ei_fb_zero_length_invalid, |
375 | 0 | "Invalid total tag length: %u", total_tag_len); |
376 | 0 | return offset + tvb_reported_length_remaining(tvb, offset); |
377 | 0 | } |
378 | 0 | return offset + total_tag_len; |
379 | |
|
380 | 0 | } |
381 | | |
382 | | |
383 | | static int |
384 | 0 | dissect_fb_zero_unencrypt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *fb_zero_tree, unsigned offset, uint8_t len_pkn _U_){ |
385 | |
|
386 | 0 | while(tvb_reported_length_remaining(tvb, offset) > 0){ |
387 | 0 | proto_item *ti; |
388 | 0 | uint32_t message_tag, tag_number, length; |
389 | 0 | const uint8_t* message_tag_str; |
390 | 0 | proto_tree_add_item(fb_zero_tree, hf_fb_zero_unknown, tvb, offset, 1, ENC_NA); |
391 | 0 | offset += 1; |
392 | | |
393 | | /* |
394 | | * XXX: in theory, a tagged message could be split between |
395 | | * TCP segments - even the *header containing the length* |
396 | | * could be split - but, from the sequence described in the |
397 | | * Facebook blog post, that's probably unlikely in practice. |
398 | | */ |
399 | 0 | proto_tree_add_item_ret_uint(fb_zero_tree, hf_fb_zero_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &length); |
400 | 0 | offset += 4; |
401 | |
|
402 | 0 | message_tag = tvb_get_ntohl(tvb, offset); |
403 | | /* check if it is a known message_tag (CHLO, SNOM...) */ |
404 | 0 | if(try_val_to_str(message_tag, message_tag_vals)){ |
405 | 0 | ti = proto_tree_add_item_ret_string(fb_zero_tree, hf_fb_zero_tag, tvb, offset, 4, ENC_ASCII|ENC_NA, pinfo->pool, &message_tag_str); |
406 | |
|
407 | 0 | proto_item_append_text(ti, ", Type: %s (%s)", message_tag_str, |
408 | 0 | val_to_str_const(message_tag, message_tag_vals, "Unknown Tag")); |
409 | 0 | col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(message_tag, message_tag_vals, "Unknown")); |
410 | 0 | offset += 4; |
411 | |
|
412 | 0 | proto_tree_add_item(fb_zero_tree, hf_fb_zero_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN); |
413 | 0 | tag_number = tvb_get_letohs(tvb, offset); |
414 | 0 | offset += 2; |
415 | |
|
416 | 0 | proto_tree_add_item(fb_zero_tree, hf_fb_zero_padding, tvb, offset, 2, ENC_NA); |
417 | 0 | offset += 2; |
418 | |
|
419 | 0 | offset = dissect_fb_zero_tag(tvb, pinfo, fb_zero_tree, offset, tag_number); |
420 | 0 | } else { |
421 | 0 | proto_tree_add_item(fb_zero_tree, hf_fb_zero_unknown, tvb, offset, length, ENC_NA); |
422 | 0 | offset += length; |
423 | 0 | } |
424 | 0 | } |
425 | |
|
426 | 0 | return offset; |
427 | |
|
428 | 0 | } |
429 | | |
430 | | static int |
431 | | dissect_fb_zero_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
432 | | void *data _U_) |
433 | 0 | { |
434 | 0 | proto_item *ti, *ti_puflags; |
435 | 0 | proto_tree *fb_zero_tree, *puflags_tree; |
436 | 0 | unsigned offset = 0; |
437 | 0 | uint8_t puflags; |
438 | 0 | uint32_t message_tag, version; |
439 | |
|
440 | 0 | if (tvb_captured_length(tvb) < FBZERO_MIN_LENGTH) |
441 | 0 | return 0; |
442 | | |
443 | | |
444 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "FB_ZERO"); |
445 | |
|
446 | 0 | ti = proto_tree_add_item(tree, proto_fb_zero, tvb, 0, -1, ENC_NA); |
447 | 0 | fb_zero_tree = proto_item_add_subtree(ti, ett_fb_zero); |
448 | | |
449 | | /* Public Flags */ |
450 | 0 | ti_puflags = proto_tree_add_item(fb_zero_tree, hf_fb_zero_puflags, tvb, offset, 1, ENC_LITTLE_ENDIAN); |
451 | 0 | puflags_tree = proto_item_add_subtree(ti_puflags, ett_fb_zero_puflags); |
452 | 0 | proto_tree_add_item(puflags_tree, hf_fb_zero_puflags_vrsn, tvb, offset, 1, ENC_NA); |
453 | 0 | proto_tree_add_item(puflags_tree, hf_fb_zero_puflags_unknown, tvb, offset, 1, ENC_LITTLE_ENDIAN); |
454 | 0 | puflags = tvb_get_uint8(tvb, offset); |
455 | 0 | offset += 1; |
456 | |
|
457 | 0 | if(puflags & PUFLAGS_VRSN){ |
458 | 0 | version = tvb_get_ntoh24(tvb, offset); |
459 | 0 | if(version == VERSION_QTV){ |
460 | 0 | proto_tree_add_item(fb_zero_tree, hf_fb_zero_version, tvb, offset, 3, ENC_ASCII); |
461 | 0 | offset += 3; |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | /* Unencrypt Message (Handshake or Connection Close...) */ |
466 | 0 | message_tag = tvb_get_ntohl(tvb, offset+5); |
467 | 0 | if (message_tag == MTAG_CHLO || message_tag == MTAG_SNOM) { |
468 | 0 | offset = dissect_fb_zero_unencrypt(tvb, pinfo, fb_zero_tree, offset, 1); |
469 | |
|
470 | 0 | }else { /* Payload... (encrypted... TODO FIX !) */ |
471 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Payload (Encrypted)"); |
472 | 0 | proto_tree_add_item(fb_zero_tree, hf_fb_zero_payload, tvb, offset, -1, ENC_NA); |
473 | 0 | } |
474 | 0 | return offset; |
475 | 0 | } |
476 | | |
477 | | |
478 | | static int |
479 | | dissect_fb_zero(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
480 | | void *data _U_) |
481 | 0 | { |
482 | 0 | return dissect_fb_zero_common(tvb, pinfo, tree, NULL); |
483 | 0 | } |
484 | | |
485 | | static bool dissect_fb_zero_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
486 | 2.88k | { |
487 | 2.88k | conversation_t *conversation = NULL; |
488 | 2.88k | int offset = 0; |
489 | 2.88k | uint32_t version, length, message_tag; |
490 | | /* Verify packet size (Flag (1 byte) + Version (3bytes) + Flag (1 byte) + length (4 bytes) + Tag (4 bytes)) */ |
491 | 2.88k | if (tvb_captured_length(tvb) < 13) |
492 | 678 | { |
493 | 678 | return false; |
494 | 678 | } |
495 | | |
496 | | /* Flag */ |
497 | 2.20k | offset += 1; |
498 | | |
499 | | /* Version */ |
500 | 2.20k | version = tvb_get_ntoh24(tvb, offset); |
501 | 2.20k | offset += 3; |
502 | | |
503 | | /* Flag */ |
504 | 2.20k | offset += 1; |
505 | | |
506 | | /* length */ |
507 | 2.20k | length = tvb_get_letohl(tvb, offset); |
508 | 2.20k | offset += 4; |
509 | | |
510 | | /* (message) tag */ |
511 | 2.20k | message_tag = tvb_get_ntohl(tvb, offset); |
512 | | |
513 | 2.20k | if (version == VERSION_QTV && length <= tvb_reported_length(tvb) && (message_tag == MTAG_CHLO || message_tag == MTAG_SNOM)) { |
514 | 0 | conversation = find_or_create_conversation(pinfo); |
515 | 0 | conversation_set_dissector(conversation, fb_zero_handle); |
516 | 0 | dissect_fb_zero(tvb, pinfo, tree, data); |
517 | 0 | return true; |
518 | 0 | } |
519 | | |
520 | 2.20k | return false; |
521 | 2.20k | } |
522 | | |
523 | | void |
524 | | proto_register_fb_zero(void) |
525 | 14 | { |
526 | 14 | static hf_register_info hf[] = { |
527 | 14 | { &hf_fb_zero_puflags, |
528 | 14 | { "Public Flags", "fb_zero.puflags", |
529 | 14 | FT_UINT8, BASE_HEX, NULL, 0x0, |
530 | 14 | "Specifying per-packet public flags", HFILL } |
531 | 14 | }, |
532 | 14 | { &hf_fb_zero_puflags_vrsn, |
533 | 14 | { "Version", "fb_zero.puflags.version", |
534 | 14 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), PUFLAGS_VRSN, |
535 | 14 | "Signifies that this packet also contains the version of the FB Zero protocol", HFILL } |
536 | 14 | }, |
537 | 14 | { &hf_fb_zero_puflags_unknown, |
538 | 14 | { "Unknown", "fb_zero.puflags.unknown", |
539 | 14 | FT_UINT8, BASE_HEX, NULL, PUFLAGS_UNKN, |
540 | 14 | NULL, HFILL } |
541 | 14 | }, |
542 | | |
543 | 14 | { &hf_fb_zero_version, |
544 | 14 | { "Version", "fb_zero.version", |
545 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
546 | 14 | "32 bit opaque tag that represents the version of the ZB Zero (Always QTV)", HFILL } |
547 | 14 | }, |
548 | 14 | { &hf_fb_zero_length, |
549 | 14 | { "Length", "fb_zero.length", |
550 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
551 | 14 | NULL, HFILL } |
552 | 14 | }, |
553 | 14 | { &hf_fb_zero_tag, |
554 | 14 | { "Tag", "fb_zero.tag", |
555 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
556 | 14 | NULL, HFILL } |
557 | 14 | }, |
558 | 14 | { &hf_fb_zero_tag_number, |
559 | 14 | { "Tag Number", "fb_zero.tag_number", |
560 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
561 | 14 | NULL, HFILL } |
562 | 14 | }, |
563 | 14 | { &hf_fb_zero_tags, |
564 | 14 | { "Tag/value", "fb_zero.tags", |
565 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
566 | 14 | NULL, HFILL } |
567 | 14 | }, |
568 | 14 | { &hf_fb_zero_tag_type, |
569 | 14 | { "Tag Type", "fb_zero.tag_type", |
570 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
571 | 14 | NULL, HFILL } |
572 | 14 | }, |
573 | 14 | { &hf_fb_zero_tag_offset_end, |
574 | 14 | { "Tag offset end", "fb_zero.tag_offset_end", |
575 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
576 | 14 | NULL, HFILL } |
577 | 14 | }, |
578 | 14 | { &hf_fb_zero_tag_length, |
579 | 14 | { "Tag length", "fb_zero.tag_offset_length", |
580 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
581 | 14 | NULL, HFILL } |
582 | 14 | }, |
583 | 14 | { &hf_fb_zero_tag_value, |
584 | 14 | { "Tag/value", "fb_zero.tag_value", |
585 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
586 | 14 | NULL, HFILL } |
587 | 14 | }, |
588 | 14 | { &hf_fb_zero_tag_sni, |
589 | 14 | { "Server Name Indication", "fb_zero.tag.sni", |
590 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
591 | 14 | "The fully qualified DNS name of the server, canonicalised to lowercase with no trailing period", HFILL } |
592 | 14 | }, |
593 | 14 | { &hf_fb_zero_tag_vers, |
594 | 14 | { "Version", "fb_zero.tag.version", |
595 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
596 | 14 | "Version of FB Zero supported", HFILL } |
597 | 14 | }, |
598 | 14 | { &hf_fb_zero_tag_sno, |
599 | 14 | { "Server nonce", "fb_zero.tag.sno", |
600 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
601 | 14 | NULL, HFILL } |
602 | 14 | }, |
603 | 14 | { &hf_fb_zero_tag_aead, |
604 | 14 | { "Authenticated encryption algorithms", "fb_zero.tag.aead", |
605 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
606 | 14 | "A list of tags, in preference order, specifying the AEAD primitives supported by the server", HFILL } |
607 | 14 | }, |
608 | 14 | { &hf_fb_zero_tag_scid, |
609 | 14 | { "Server Config ID", "fb_zero.tag.scid", |
610 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
611 | 14 | "An opaque, 16-byte identifier for this server config", HFILL } |
612 | 14 | }, |
613 | 14 | { &hf_fb_zero_tag_time, |
614 | 14 | { "Time", "fb_zero.tag.time", |
615 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
616 | 14 | NULL, HFILL } |
617 | 14 | }, |
618 | 14 | { &hf_fb_zero_tag_alpn, |
619 | 14 | { "ALPN", "fb_zero.tag.alpn", |
620 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
621 | 14 | "Application-Layer Protocol Negotiation supported", HFILL } |
622 | 14 | }, |
623 | 14 | { &hf_fb_zero_tag_pubs, |
624 | 14 | { "Public value", "fb_zero.tag.pubs", |
625 | 14 | FT_UINT24, BASE_DEC_HEX, NULL, 0x0, |
626 | 14 | "A list of public values, 24-bit, little-endian length prefixed", HFILL } |
627 | 14 | }, |
628 | 14 | { &hf_fb_zero_tag_kexs, |
629 | 14 | { "Key exchange algorithms", "fb_zero.tag.kexs", |
630 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
631 | 14 | "A list of tags, in preference order, specifying the key exchange algorithms that the server supports", HFILL } |
632 | 14 | }, |
633 | 14 | { &hf_fb_zero_tag_nonc, |
634 | 14 | { "Client nonce", "fb_zero.tag.nonc", |
635 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
636 | 14 | "32 bytes consisting of 4 bytes of timestamp (big-endian, UNIX epoch seconds), 8 bytes of server orbit and 20 bytes of random data", HFILL } |
637 | 14 | }, |
638 | 14 | { &hf_fb_zero_tag_unknown, |
639 | 14 | { "Unknown tag", "fb_zero.tag.unknown", |
640 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
641 | 14 | NULL, HFILL } |
642 | 14 | }, |
643 | 14 | { &hf_fb_zero_padding, |
644 | 14 | { "Padding", "fb_zero.padding", |
645 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
646 | 14 | NULL, HFILL } |
647 | 14 | }, |
648 | 14 | { &hf_fb_zero_payload, |
649 | 14 | { "Payload", "fb_zero.payload", |
650 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
651 | 14 | "Fb Zero Payload..", HFILL } |
652 | 14 | }, |
653 | 14 | { &hf_fb_zero_unknown, |
654 | 14 | { "Unknown", "fb_zero.unknown", |
655 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
656 | 14 | "Unknown Data", HFILL } |
657 | 14 | }, |
658 | 14 | }; |
659 | | |
660 | | |
661 | 14 | static int *ett[] = { |
662 | 14 | &ett_fb_zero, |
663 | 14 | &ett_fb_zero_puflags, |
664 | 14 | &ett_fb_zero_prflags, |
665 | 14 | &ett_fb_zero_ft, |
666 | 14 | &ett_fb_zero_ftflags, |
667 | 14 | &ett_fb_zero_tag_value |
668 | 14 | }; |
669 | | |
670 | 14 | static ei_register_info ei[] = { |
671 | 14 | { &ei_fb_zero_tag_undecoded, { "fb_zero.tag.undecoded", PI_UNDECODED, PI_NOTE, "Dissector for FB Zero Tag code not implemented, Contact Wireshark developers if you want this supported", EXPFILL }}, |
672 | 14 | { &ei_fb_zero_tag_offset_end_invalid, { "fb_zero.offset_end.invalid", PI_PROTOCOL, PI_ERROR, "Invalid tag end offset", EXPFILL }}, |
673 | 14 | { &ei_fb_zero_length_invalid, { "fb_zero.length.invalid", PI_PROTOCOL, PI_WARN, "Invalid length", EXPFILL }}, |
674 | 14 | }; |
675 | | |
676 | 14 | expert_module_t *expert_fb_zero; |
677 | | |
678 | 14 | proto_fb_zero = proto_register_protocol("(Facebook) Zero Protocol", "FBZERO", "fb_zero"); |
679 | | |
680 | 14 | fb_zero_handle = register_dissector("fb_zero", dissect_fb_zero, proto_fb_zero); |
681 | | |
682 | 14 | proto_register_field_array(proto_fb_zero, hf, array_length(hf)); |
683 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
684 | | |
685 | 14 | expert_fb_zero = expert_register_protocol(proto_fb_zero); |
686 | 14 | expert_register_field_array(expert_fb_zero, ei, array_length(ei)); |
687 | 14 | } |
688 | | |
689 | | void |
690 | | proto_reg_handoff_fb_zero(void) |
691 | 14 | { |
692 | | |
693 | 14 | heur_dissector_add("tcp", dissect_fb_zero_heur, "FBZero (QUIC) over TCP", "fb_zero", proto_fb_zero, HEURISTIC_ENABLE); |
694 | | |
695 | 14 | } |
696 | | |
697 | | |
698 | | /* |
699 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
700 | | * |
701 | | * Local variables: |
702 | | * c-basic-offset: 4 |
703 | | * tab-width: 8 |
704 | | * indent-tabs-mode: nil |
705 | | * End: |
706 | | * |
707 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
708 | | * :indentSize=4:tabSize=8:noTabs=true: |
709 | | */ |