Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-mc-nmf.c
blob3448e1b0342d285911f55c998331981e0f03021c
1 /* packet-mc-nmf.c
2 * Routines for .NET Message Framing Protocol (MC-NMF) dissection
3 * Copyright 2017-2020, Uli Heilmeier <uh@heilmeier.eu>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wieshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * Basic dissector for .NET Message Framing Protocol based on protocol reference found at
14 * https://download.microsoft.com/download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/[MC-NMF].pdf
15 * https://msdn.microsoft.com/en-us/library/cc219293.aspx
17 * Things missing:
18 * - heuristic to detect .NET MFP
21 #include <config.h>
23 #include <epan/packet.h>
24 #include <epan/conversation.h>
25 #include <epan/expert.h>
27 /* Prototypes */
28 void proto_reg_handoff_mc_nmf(void);
29 void proto_register_mc_nmf(void);
31 static dissector_handle_t ms_nns_handle;
32 static dissector_handle_t tls_handle;
34 static dissector_handle_t mc_nmf_handle;
36 /* Initialize the protocol and registered fields */
38 #define MC_NMF_REC_VERSION 0
39 #define MC_NMF_REC_MODE 1
40 #define MC_NMF_REC_VIA 2
41 #define MC_NMF_REC_KNOWN_ENC 3
42 #define MC_NMF_REC_EXT_ENC 4
43 #define MC_NMF_REC_UNSIZED_ENV 5
44 #define MC_NMF_REC_SIZED_ENV 6
45 #define MC_NMF_REC_END 7
46 #define MC_NMF_REC_FAULT 8
47 #define MC_NMF_REC_UPGRADE_REQ 9
48 #define MC_NMF_REC_UPGRADE_RSP 10
49 #define MC_NMF_REC_PREAMBLE_ACK 11
50 #define MC_NMF_REC_PREAMBLE_END 12
52 static const value_string mc_nmf_record_type_vals[] = {
53 { MC_NMF_REC_VERSION, "Version Record" },
54 { MC_NMF_REC_MODE, "Mode Record" },
55 { MC_NMF_REC_VIA, "Via Record" },
56 { MC_NMF_REC_KNOWN_ENC, "Known Encoding Record" },
57 { MC_NMF_REC_EXT_ENC, "Extensible Encoding Record" },
58 { MC_NMF_REC_UNSIZED_ENV, "Unsized Envelope Record" },
59 { MC_NMF_REC_SIZED_ENV, "Sized Envelope Record" },
60 { MC_NMF_REC_END, "End Record" },
61 { MC_NMF_REC_FAULT, "Fault Record" },
62 { MC_NMF_REC_UPGRADE_REQ, "Upgrade Request Record" },
63 { MC_NMF_REC_UPGRADE_RSP, "Upgrade Response Record" },
64 { MC_NMF_REC_PREAMBLE_ACK, "Preamble Ack Record" },
65 { MC_NMF_REC_PREAMBLE_END, "Preamble End Record" },
66 { 0, NULL}
69 static const value_string mc_nmf_mode_vals[] = {
70 { 1, "Singleton-Unsized" },
71 { 2, "Duplex" },
72 { 3, "Simplex" },
73 { 4, "Singleton-Sized" },
74 { 0, NULL }
77 static const value_string mc_nmf_encoding_vals[] = {
78 { 0, "UTF-8" },
79 { 1, "UTF-16" },
80 { 2, "Unicode little-endian" },
81 { 3, "UTF-8" },
82 { 4, "UTF-16" },
83 { 5, "Unicode little-endian" },
84 { 6, "MTOM" },
85 { 7, "Binary" },
86 { 8, "Binary with in-band dictionary" },
87 { 0, NULL }
90 struct mc_nmf_session_state {
91 bool upgrade_req;
92 bool negotiate;
93 bool tls;
94 uint32_t upgrade_rsp;
97 static int proto_mc_nmf;
98 static int hf_mc_nmf_record_type;
99 static int hf_mc_nmf_major_version;
100 static int hf_mc_nmf_minor_version;
101 static int hf_mc_nmf_mode;
102 static int hf_mc_nmf_known_encoding;
103 static int hf_mc_nmf_via_length;
104 static int hf_mc_nmf_via;
105 static int hf_mc_nmf_encoding_length;
106 static int hf_mc_nmf_encoding_type;
107 static int hf_mc_nmf_fault_length;
108 static int hf_mc_nmf_fault;
109 static int hf_mc_nmf_upgrade_length;
110 static int hf_mc_nmf_upgrade;
111 static int hf_mc_nmf_chunk_length;
112 static int hf_mc_nmf_chunk;
113 static int hf_mc_nmf_terminator;
114 static int hf_mc_nmf_payload_length;
115 static int hf_mc_nmf_payload;
116 static int hf_mc_nmf_upgrade_proto_data;
118 static expert_field ei_mc_nmf_size_too_big;
120 // [MC-NMF] does not have a defined port https://learn.microsoft.com/en-us/openspecs/windows_protocols/mc-nmf/51b5eb53-f488-4b74-b21d-8a498f016b61
121 // but 9389 is ADWS port https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adcap/cfff3d7f-e7cd-4529-86a0-4de89efe3855
122 // which relies on [MC-NMF], so by doing this, all ADWS traffic on port 9389 is properly dissected by default
123 #define MC_NMF_TCP_PORT 9389
125 /* Initialize the subtree pointers */
126 static int ett_mc_nmf;
127 static int ett_mc_nmf_rec;
129 #define MC_NMF_MIN_LENGTH 1
131 static bool get_size_length(tvbuff_t *tvb, int *offset, unsigned *len_length, packet_info *pinfo, uint32_t *out_size) {
132 uint8_t lbyte;
133 uint64_t size = 0;
134 unsigned shiftcount = 0;
136 lbyte = tvb_get_uint8(tvb, *offset);
137 *offset += 1;
138 *len_length += 1;
139 size = ( lbyte & 0x7F);
140 while ( lbyte & 0x80 ) {
141 lbyte = tvb_get_uint8(tvb, *offset);
142 *offset += 1;
143 /* Guard against the pathological case of a sequence of 0x80
144 * bytes (which add nothing to size).
146 if (*len_length >= 5) {
147 expert_add_info(pinfo, NULL, &ei_mc_nmf_size_too_big);
148 return false;
150 shiftcount = 7 * *len_length;
151 size = ((lbyte & UINT64_C(0x7F)) << shiftcount) | (size);
152 *len_length += 1;
154 * Check if size if is too big to prevent against overflow.
155 * According to spec an implementation SHOULD support record sizes as
156 * large as 0xffffffff octets (encoded size requires five octets).
158 if (size > 0xffffffff) {
159 expert_add_info(pinfo, NULL, &ei_mc_nmf_size_too_big);
160 return false;
163 *out_size = (uint32_t)size;
164 return true;
167 static int
168 dissect_mc_nmf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
170 proto_item *ti, *rti, *dti;
171 proto_tree *mc_nmf_tree, *rec_tree, *data_tree;
172 unsigned offset = 0;
173 uint32_t record_type;
174 uint8_t *upgrade_protocol;
175 unsigned len_length;
176 int32_t size;
177 uint8_t search_terminator;
178 conversation_t *conversation;
179 tvbuff_t *nt_tvb;
180 struct mc_nmf_session_state *session_state;
182 if (tvb_reported_length(tvb) < MC_NMF_MIN_LENGTH)
183 return 0;
185 conversation = find_or_create_conversation(pinfo);
187 session_state = (struct mc_nmf_session_state *)conversation_get_proto_data(conversation, proto_mc_nmf);
189 if (!session_state) {
190 session_state = wmem_new0(wmem_file_scope(), struct mc_nmf_session_state);
192 conversation_add_proto_data(conversation, proto_mc_nmf, session_state);
196 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MC-NMF");
197 col_clear(pinfo->cinfo, COL_INFO);
199 ti = proto_tree_add_item(tree, proto_mc_nmf, tvb, 0, -1, ENC_NA);
201 mc_nmf_tree = proto_item_add_subtree(ti, ett_mc_nmf);
203 if ( session_state->upgrade_rsp && session_state->upgrade_rsp < pinfo->num && session_state->negotiate) {
204 dti = proto_tree_add_item(mc_nmf_tree, hf_mc_nmf_upgrade_proto_data, tvb,
205 offset, -1, ENC_NA);
206 data_tree = proto_item_add_subtree(dti, ett_mc_nmf_rec);
207 nt_tvb = tvb_new_subset_remaining(tvb, offset);
208 call_dissector(ms_nns_handle, nt_tvb, pinfo, data_tree);
209 return offset + tvb_reported_length(nt_tvb);
211 else if ( session_state->upgrade_rsp && session_state->upgrade_rsp < pinfo->num && session_state->tls) {
212 dti = proto_tree_add_item(mc_nmf_tree, hf_mc_nmf_upgrade_proto_data, tvb,
213 offset, -1, ENC_NA);
214 data_tree = proto_item_add_subtree(dti, ett_mc_nmf_rec);
215 nt_tvb = tvb_new_subset_remaining(tvb, offset);
216 call_dissector(tls_handle, nt_tvb, pinfo, data_tree);
217 return offset + tvb_reported_length(nt_tvb);
220 while (tvb_reported_length(tvb) > offset)
222 rti = proto_tree_add_item_ret_uint(mc_nmf_tree, hf_mc_nmf_record_type, tvb,
223 offset, 1, ENC_BIG_ENDIAN, &record_type);
224 offset += 1;
225 col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s", val_to_str_const(record_type, mc_nmf_record_type_vals, "Unknown Record"));
227 switch (record_type) {
228 case MC_NMF_REC_VERSION:
229 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
230 proto_tree_add_item(rec_tree, hf_mc_nmf_major_version, tvb,
231 offset, 1, ENC_BIG_ENDIAN);
232 offset += 1;
233 proto_tree_add_item(rec_tree, hf_mc_nmf_minor_version, tvb,
234 offset, 1, ENC_BIG_ENDIAN);
235 offset += 1;
236 break;
237 case MC_NMF_REC_MODE:
238 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
239 proto_tree_add_item(rec_tree, hf_mc_nmf_mode, tvb,
240 offset, 1, ENC_BIG_ENDIAN);
241 offset += 1;
242 break;
243 case MC_NMF_REC_VIA:
244 size = 0;
245 len_length = 0;
246 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
247 if (!get_size_length(tvb, &offset, &len_length, pinfo, &size))
248 return tvb_reported_length(tvb);
249 proto_tree_add_uint(rec_tree, hf_mc_nmf_via_length, tvb, offset - len_length, len_length, size);
250 proto_tree_add_item(rec_tree, hf_mc_nmf_via, tvb, offset, size, ENC_UTF_8);
251 offset += size;
252 break;
253 case MC_NMF_REC_KNOWN_ENC:
254 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
255 proto_tree_add_item(rec_tree, hf_mc_nmf_known_encoding, tvb, offset, 1, ENC_BIG_ENDIAN);
256 offset += 1;
257 break;
258 case MC_NMF_REC_EXT_ENC:
259 size = 0;
260 len_length = 0;
261 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
262 if (!get_size_length(tvb, &offset, &len_length, pinfo, &size))
263 return tvb_reported_length(tvb);
264 proto_tree_add_uint(rec_tree, hf_mc_nmf_encoding_length, tvb, offset - len_length, len_length, size);
265 proto_tree_add_item(rec_tree, hf_mc_nmf_encoding_type, tvb, offset, size, ENC_UTF_8);
266 offset += size;
267 break;
268 case MC_NMF_REC_UNSIZED_ENV:
269 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
270 do {
271 size = 0;
272 len_length = 0;
273 if (!get_size_length(tvb, &offset, &len_length, pinfo, &size))
274 return tvb_reported_length(tvb);
275 proto_tree_add_uint(rec_tree, hf_mc_nmf_chunk_length, tvb, offset - len_length, len_length, size);
276 proto_tree_add_item(rec_tree, hf_mc_nmf_chunk, tvb, offset, size, ENC_NA);
277 offset += size;
278 search_terminator = tvb_get_uint8(tvb, offset);
279 } while ( search_terminator != 0x00 );
280 proto_tree_add_item(rec_tree, hf_mc_nmf_terminator, tvb,
281 offset, 1, ENC_NA);
282 offset += 1;
283 break;
284 case MC_NMF_REC_SIZED_ENV:
285 size = 0;
286 len_length = 0;
287 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
288 if (!get_size_length(tvb, &offset, &len_length, pinfo, &size))
289 return tvb_reported_length(tvb);
290 proto_tree_add_uint(rec_tree, hf_mc_nmf_payload_length, tvb, offset - len_length, len_length, size);
291 proto_tree_add_item(rec_tree, hf_mc_nmf_payload, tvb, offset, size, ENC_NA);
292 offset += size;
293 break;
294 case MC_NMF_REC_FAULT:
295 size = 0;
296 len_length = 0;
297 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
298 if (!get_size_length(tvb, &offset, &len_length, pinfo, &size))
299 return tvb_reported_length(tvb);
300 proto_tree_add_uint(rec_tree, hf_mc_nmf_fault_length, tvb, offset - len_length, len_length, size);
301 proto_tree_add_item(rec_tree, hf_mc_nmf_fault, tvb, offset, size, ENC_UTF_8);
302 offset += size;
303 break;
304 case MC_NMF_REC_UPGRADE_REQ:
305 size = 0;
306 len_length = 0;
307 rec_tree = proto_item_add_subtree(rti, ett_mc_nmf_rec);
308 if (!get_size_length(tvb, &offset, &len_length, pinfo, &size))
309 return tvb_reported_length(tvb);
310 proto_tree_add_uint(rec_tree, hf_mc_nmf_upgrade_length, tvb, offset - len_length, len_length, size);
311 proto_tree_add_item(rec_tree, hf_mc_nmf_upgrade, tvb, offset, size, ENC_UTF_8);
312 upgrade_protocol = tvb_get_string_enc(pinfo->pool, tvb, offset, size, ENC_UTF_8|ENC_NA);
313 offset += size;
314 if (strcmp((char*)upgrade_protocol, "application/negotiate") == 0) {
315 session_state->negotiate = true;
317 else if (strcmp((char*)upgrade_protocol, "application/ssl-tls") == 0) {
318 session_state->tls = true;
320 session_state->upgrade_req = true;
321 break;
322 case MC_NMF_REC_UPGRADE_RSP:
323 if ( session_state->upgrade_req == true) {
324 session_state->upgrade_rsp = pinfo->num;
326 break;
327 case MC_NMF_REC_END:
328 case MC_NMF_REC_PREAMBLE_ACK:
329 case MC_NMF_REC_PREAMBLE_END:
330 break;
333 return offset;
336 void proto_register_mc_nmf(void)
338 static hf_register_info hf[] = {
339 { &hf_mc_nmf_record_type,
340 { "RecordType", "mc-nmf.record_type",
341 FT_UINT8, BASE_DEC, VALS(mc_nmf_record_type_vals), 0x0,
342 NULL, HFILL }
344 { &hf_mc_nmf_major_version,
345 { "Major Version", "mc-nmf.major_version",
346 FT_UINT8, BASE_DEC, NULL, 0x0,
347 NULL, HFILL }
349 { &hf_mc_nmf_minor_version,
350 { "Minor Version", "mc-nmf.minor_version",
351 FT_UINT8, BASE_DEC, NULL, 0x0,
352 NULL, HFILL }
354 { &hf_mc_nmf_mode,
355 { "Mode", "mc-nmf.mode",
356 FT_UINT8, BASE_DEC, VALS(mc_nmf_mode_vals), 0x0,
357 NULL, HFILL }
359 { &hf_mc_nmf_known_encoding,
360 { "Known Encoding", "mc-nmf.known_encoding",
361 FT_UINT8, BASE_DEC, VALS(mc_nmf_encoding_vals), 0x0,
362 NULL, HFILL }
364 { &hf_mc_nmf_via_length,
365 { "Via Length", "mc-nmf.via_length",
366 FT_UINT32, BASE_DEC, NULL, 0x0,
367 NULL, HFILL }
369 { &hf_mc_nmf_via,
370 { "Via", "mc-nmf.via",
371 FT_STRING, BASE_NONE, NULL, 0x0,
372 NULL, HFILL }
374 { &hf_mc_nmf_encoding_length,
375 { "Encoding Length", "mc-nmf.encoding_length",
376 FT_UINT32, BASE_DEC, NULL, 0x0,
377 NULL, HFILL }
379 { &hf_mc_nmf_encoding_type,
380 { "Encoding Type", "mc-nmf.encoding_type",
381 FT_STRING, BASE_NONE, NULL, 0x0,
382 "MIME Content-Type", HFILL }
384 { &hf_mc_nmf_fault_length,
385 { "Fault Length", "mc-nmf.fault_length",
386 FT_UINT32, BASE_DEC, NULL, 0x0,
387 NULL, HFILL }
389 { &hf_mc_nmf_fault,
390 { "Fault", "mc-nmf.fault",
391 FT_STRING, BASE_NONE, NULL, 0x0,
392 NULL, HFILL }
394 { &hf_mc_nmf_upgrade_length,
395 { "Upgrade Protocol Length", "mc-nmf.upgrade_length",
396 FT_UINT32, BASE_DEC, NULL, 0x0,
397 NULL, HFILL }
399 { &hf_mc_nmf_upgrade,
400 { "Upgrade Protocol", "mc-nmf.upgrade",
401 FT_STRING, BASE_NONE, NULL, 0x0,
402 NULL, HFILL }
404 { &hf_mc_nmf_chunk_length,
405 { "DataChunk Length", "mc-nmf.chunk_length",
406 FT_UINT32, BASE_DEC, NULL, 0x0,
407 NULL, HFILL }
409 { &hf_mc_nmf_chunk,
410 { "DataChunk", "mc-nmf.chunk",
411 FT_BYTES, BASE_NONE, NULL, 0x0,
412 NULL, HFILL }
414 { &hf_mc_nmf_terminator,
415 { "Terminator", "mc-nmf.terminator",
416 FT_BYTES, BASE_NONE, NULL, 0x0,
417 NULL, HFILL }
419 { &hf_mc_nmf_payload_length,
420 { "Size", "mc-nmf.payload_length",
421 FT_UINT32, BASE_DEC, NULL, 0x0,
422 NULL, HFILL }
424 { &hf_mc_nmf_payload,
425 { "Payload", "mc-nmf.payload",
426 FT_BYTES, BASE_NONE, NULL, 0x0,
427 NULL, HFILL }
429 { &hf_mc_nmf_upgrade_proto_data,
430 { "Upgrade Protocol Data", "mc-nmf.upgrade_protocol_data",
431 FT_NONE, BASE_NONE, NULL, 0x0,
432 NULL, HFILL }
436 static int *ett[] = {
437 &ett_mc_nmf,
438 &ett_mc_nmf_rec
441 static ei_register_info ei[] = {
442 { &ei_mc_nmf_size_too_big, { "mc-nmf.size_too_big", PI_MALFORMED, PI_ERROR, "Size too big", EXPFILL }},
445 expert_module_t* expert_mc_nmf;
447 proto_mc_nmf = proto_register_protocol(".NET Message Framing Protocol", "MC-NMF", "mc-nmf");
449 proto_register_field_array(proto_mc_nmf, hf, array_length(hf));
450 proto_register_subtree_array(ett, array_length(ett));
451 expert_mc_nmf = expert_register_protocol(proto_mc_nmf);
452 expert_register_field_array(expert_mc_nmf, ei, array_length(ei));
454 mc_nmf_handle = register_dissector("mc-nmf", dissect_mc_nmf, proto_mc_nmf);
457 void proto_reg_handoff_mc_nmf(void)
459 dissector_add_uint_with_preference("tcp.port", MC_NMF_TCP_PORT, mc_nmf_handle);
460 ms_nns_handle = find_dissector_add_dependency("ms-nns", proto_mc_nmf);
461 tls_handle = find_dissector("tls");
465 * Editor modelines - https://www.wireshark.org/tools/modelines.html
467 * Local variables:
468 * c-basic-offset: 4
469 * tab-width: 8
470 * indent-tabs-mode: nil
471 * End:
473 * vi: set shiftwidth=4 tabstop=8 expandtab:
474 * :indentSize=4:tabSize=8:noTabs=true: