/src/wireshark/epan/dissectors/packet-nmf.c
Line | Count | Source |
1 | | /* |
2 | | * packet-nmf.c |
3 | | * |
4 | | * Routines for [MC-NMF] .NET Message Framing Protocol |
5 | | * |
6 | | * Copyright 2017 Stefan Metzmacher <metze@samba.org> |
7 | | * |
8 | | * SPDX-License-Identifier: GPL-2.0-or-later |
9 | | */ |
10 | | |
11 | | #include "config.h" |
12 | | |
13 | | #include <wsutil/str_util.h> |
14 | | #include <epan/packet.h> |
15 | | #include <epan/prefs.h> |
16 | | #include <epan/expert.h> |
17 | | #include <epan/proto_data.h> |
18 | | #include "packet-tcp.h" |
19 | | #include "packet-windows-common.h" |
20 | | #include "packet-gssapi.h" |
21 | | |
22 | 16 | #define NMF_PORT 9389 |
23 | | |
24 | | void proto_register_nmf(void); |
25 | | void proto_reg_handoff_nmf(void); |
26 | | |
27 | | static dissector_handle_t gssapi_handle; |
28 | | static dissector_handle_t gssapi_wrap_handle; |
29 | | |
30 | | static dissector_handle_t xml_handle; |
31 | | |
32 | | static int proto_nmf; |
33 | | |
34 | | static int ett_nmf; |
35 | | static int ett_nmf_payload; |
36 | | |
37 | | static int hf_nmf_record; |
38 | | static int hf_nmf_record_type; |
39 | | static int hf_nmf_version_major; |
40 | | static int hf_nmf_version_minor; |
41 | | static int hf_nmf_mode_value; |
42 | | static int hf_nmf_via_length; |
43 | | static int hf_nmf_via_value; |
44 | | static int hf_nmf_known_mode_value; |
45 | | static int hf_nmf_sized_envelope_length; |
46 | | static int hf_nmf_upgrade_length; |
47 | | static int hf_nmf_upgrade_protocol; |
48 | | static int hf_nmf_negotiate_type; |
49 | | static int hf_nmf_negotiate_length; |
50 | | static int hf_nmf_protect_length; |
51 | | |
52 | | static expert_field ei_nmf_bad_record_size; |
53 | | |
54 | | static bool nmf_reassemble = true; |
55 | | |
56 | | enum nmf_record_type { |
57 | | NMF_VERSION_RECORD = 0x00, |
58 | | NMF_MODE_RECORD = 0x01, |
59 | | NMF_VIA_RECORD = 0x02, |
60 | | NMF_KNOWN_ENCODING_RECORD = 0x03, |
61 | | NMF_EXTENSIBLE_ENCODING_RECORD = 0x04, |
62 | | NMF_UNSIZED_ENVELOPE_RECORD = 0x05, |
63 | | NMF_SIZED_ENVELOPE_RECORD = 0x06, |
64 | | NMF_END_RECORD = 0x07, |
65 | | NMF_FAULT_RECORD = 0x08, |
66 | | NMF_UPGRADE_REQUEST_RECORD = 0x09, |
67 | | NMF_UPGRADE_RESPONSE_RECORD = 0x0A, |
68 | | NMF_PREAMBLE_ACK_RECORD = 0x0B, |
69 | | NMF_PREAMBLE_END_RECORD = 0x0C |
70 | | }; |
71 | | |
72 | | static const value_string record_types[] = { |
73 | | { NMF_VERSION_RECORD, "Version Record"}, |
74 | | { NMF_MODE_RECORD, "Mode Record"}, |
75 | | { NMF_VIA_RECORD, "Via Record"}, |
76 | | { NMF_KNOWN_ENCODING_RECORD, "Known Encoding Record"}, |
77 | | { NMF_EXTENSIBLE_ENCODING_RECORD, "Extensible Encoding Record"}, |
78 | | { NMF_UNSIZED_ENVELOPE_RECORD, "Unsized Envelope Record"}, |
79 | | { NMF_SIZED_ENVELOPE_RECORD, "Sized Envelope Record"}, |
80 | | { NMF_END_RECORD, "End Record"}, |
81 | | { NMF_FAULT_RECORD, "Fault Record"}, |
82 | | { NMF_UPGRADE_REQUEST_RECORD, "Upgrade Request Record"}, |
83 | | { NMF_UPGRADE_RESPONSE_RECORD, "Upgrade Response Record"}, |
84 | | { NMF_PREAMBLE_ACK_RECORD, "Preamble Ack Record"}, |
85 | | { NMF_PREAMBLE_END_RECORD, "Preamble End Record"}, |
86 | | { 0, NULL } |
87 | | }; |
88 | | |
89 | | static const value_string mode_values[] = { |
90 | | { 0x01, "Singleton-Unsized"}, |
91 | | { 0x02, "Duplex"}, |
92 | | { 0x03, "Simplex"}, |
93 | | { 0, NULL } |
94 | | }; |
95 | | |
96 | | static const value_string known_mode_values[] = { |
97 | | { 0x00, "SOAP 1.1 UTF-8"}, |
98 | | { 0x01, "SOAP 1.1 UTF-16"}, |
99 | | { 0x02, "SOAP 1.1 Unicode Little-Endian"}, |
100 | | { 0x03, "SOAP 1.2 UTF-8"}, |
101 | | { 0x04, "SOAP 1.2 UTF-16"}, |
102 | | { 0x05, "SOAP 1.2 Unicode Little-Endian"}, |
103 | | { 0x06, "SOAP 1.2 MOTM"}, |
104 | | { 0x07, "SOAP 1.2 Binary"}, |
105 | | { 0x08, "SOAP 1.2 Binary with in-band dictionary"}, |
106 | | { 0, NULL } |
107 | | }; |
108 | | |
109 | | typedef struct nmf_conv_info_t { |
110 | | uint32_t fnum_upgraded; |
111 | | uint32_t fnum_negotiated; |
112 | | } nmf_conv_info_t; |
113 | | |
114 | | /* Read the the varint holding the record size. |
115 | | * Callers MUST check for a 0 return value, which indicates that |
116 | | * parsing the varint failed, to avoid infinite loops. |
117 | | */ |
118 | | static int |
119 | | dissect_nmf_record_size(tvbuff_t *tvb, proto_tree *tree, |
120 | | int hf_index, int offset, uint32_t *_size) |
121 | 344 | { |
122 | 344 | uint64_t size = 0; |
123 | 344 | int start_offset = offset; |
124 | | |
125 | | /* 5 is the encoded size for UINT32_MAX (but can also contain larger |
126 | | * varints). |
127 | | */ |
128 | 344 | unsigned len = tvb_get_varint(tvb, offset, 5, &size, ENC_VARINT_PROTOBUF); |
129 | 344 | if (len == 0) { |
130 | 9 | proto_tree_add_expert_format(tree, NULL, &ei_nmf_bad_record_size, tvb, offset, 5, |
131 | 9 | "Invalid record size; varint does not end in five bytes"); |
132 | 9 | return 0; |
133 | 9 | } |
134 | 335 | if (size > UINT32_MAX) { |
135 | 2 | proto_tree_add_expert_format(tree, NULL, &ei_nmf_bad_record_size, tvb, offset, len, |
136 | 2 | "Invalid record size %" PRIu64, size); |
137 | 2 | return 0; |
138 | 2 | } |
139 | | |
140 | 333 | if (_size != NULL) { |
141 | 333 | *_size = (uint32_t)size; |
142 | 333 | } |
143 | | |
144 | 333 | if (tree != NULL && hf_index != -1) { |
145 | 333 | proto_item *item = NULL; |
146 | 333 | item = proto_tree_add_item(tree, hf_index, tvb, |
147 | 333 | start_offset, -1, ENC_NA); |
148 | 333 | proto_item_set_len(item, (int)len); |
149 | 333 | proto_item_append_text(item, ": %u (0x%x)", |
150 | 333 | (unsigned)size, (unsigned)size); |
151 | 333 | } |
152 | | |
153 | 333 | return offset; |
154 | 335 | } |
155 | | |
156 | | static int |
157 | | dissect_nmf_record(tvbuff_t *tvb, packet_info *pinfo, |
158 | | nmf_conv_info_t *nmf_info, |
159 | | proto_tree *tree, int offset) |
160 | 7.30k | { |
161 | 7.30k | proto_item *record_item = NULL; |
162 | 7.30k | proto_tree *record_tree = NULL; |
163 | 7.30k | const char *record_name = NULL; |
164 | 7.30k | enum nmf_record_type record_type; |
165 | 7.30k | uint32_t size = 0; |
166 | 7.30k | const uint8_t *str = NULL; |
167 | 7.30k | tvbuff_t *payload_tvb = NULL; |
168 | | |
169 | 7.30k | record_item = proto_tree_add_item(tree, hf_nmf_record, tvb, offset, -1, ENC_NA); |
170 | 7.30k | proto_item_append_text(record_item, ", start_offset=0x%x, ", (unsigned)offset); |
171 | 7.30k | record_tree = proto_item_add_subtree(record_item, ett_nmf); |
172 | | |
173 | 7.30k | record_type = (enum nmf_record_type)tvb_get_uint8(tvb, offset); |
174 | 7.30k | record_name = val_to_str_const((uint32_t)record_type, record_types, |
175 | 7.30k | "Unknown Record"); |
176 | 7.30k | proto_tree_add_item(record_tree, hf_nmf_record_type, |
177 | 7.30k | tvb, offset, 1, ENC_NA); |
178 | 7.30k | offset += 1; |
179 | | |
180 | 7.30k | col_append_str(pinfo->cinfo, COL_INFO, record_name); |
181 | 7.30k | proto_item_append_text(record_item, "%s", record_name); |
182 | | |
183 | 7.30k | switch (record_type) { |
184 | 1.25k | case NMF_VERSION_RECORD: |
185 | 1.25k | proto_tree_add_item(record_tree, hf_nmf_version_major, |
186 | 1.25k | tvb, offset, 1, ENC_NA); |
187 | 1.25k | offset += 1; |
188 | 1.25k | proto_tree_add_item(record_tree, hf_nmf_version_minor, |
189 | 1.25k | tvb, offset, 1, ENC_NA); |
190 | 1.25k | offset += 1; |
191 | 1.25k | break; |
192 | 339 | case NMF_MODE_RECORD: |
193 | 339 | proto_tree_add_item(record_tree, hf_nmf_mode_value, |
194 | 339 | tvb, offset, 1, ENC_NA); |
195 | 339 | offset += 1; |
196 | 339 | break; |
197 | 128 | case NMF_VIA_RECORD: |
198 | 128 | offset = dissect_nmf_record_size(tvb, record_tree, |
199 | 128 | hf_nmf_via_length, |
200 | 128 | offset, &size); |
201 | 128 | if (offset <= 0) { |
202 | 6 | return -1; |
203 | 6 | } |
204 | | |
205 | 122 | proto_tree_add_item_ret_string(record_tree, hf_nmf_via_value, |
206 | 122 | tvb, offset, size, ENC_UTF_8, |
207 | 122 | pinfo->pool, &str); |
208 | 122 | offset += size; |
209 | 122 | proto_item_append_text(record_item, ": %s", (const char *)str); |
210 | 122 | break; |
211 | 124 | case NMF_KNOWN_ENCODING_RECORD: |
212 | 124 | proto_tree_add_item(record_tree, hf_nmf_known_mode_value, |
213 | 124 | tvb, offset, 1, ENC_NA); |
214 | 124 | offset += 1; |
215 | 124 | break; |
216 | 37 | case NMF_EXTENSIBLE_ENCODING_RECORD: |
217 | | /* TODO */ |
218 | 37 | break; |
219 | 148 | case NMF_UNSIZED_ENVELOPE_RECORD: |
220 | | /* TODO */ |
221 | 148 | break; |
222 | 173 | case NMF_SIZED_ENVELOPE_RECORD: |
223 | 173 | offset = dissect_nmf_record_size(tvb, record_tree, |
224 | 173 | hf_nmf_sized_envelope_length, |
225 | 173 | offset, &size); |
226 | 173 | if (offset <= 0) { |
227 | 2 | return -1; |
228 | 2 | } |
229 | | |
230 | 171 | payload_tvb = tvb_new_subset_length(tvb, offset, size); |
231 | 171 | offset += size; |
232 | 171 | proto_item_append_text(record_item, ": Payload (%u byte%s)", |
233 | 171 | size, plurality(size, "", "s")); |
234 | 171 | proto_tree_add_format_text(record_tree, payload_tvb, 0, size); |
235 | | #if 0 |
236 | | tvbuff_t *xml_tvb = NULL; |
237 | | if (0) { |
238 | | /* TODO: |
239 | | * |
240 | | * 1. reassemble payload |
241 | | * 2. use |
242 | | * [MC-NBFSE] .NET Binary Format: SOAP Extension |
243 | | * [MC-NBFS] .NET Binary Format: SOAP Data Structure |
244 | | * [MC-NBFX] .NET Binary Format: XML Data Structure |
245 | | * to generate XML |
246 | | * 3. call the XML dissector. |
247 | | */ |
248 | | if (payload_tvb != NULL) { |
249 | | xml_tvb = NULL; |
250 | | } |
251 | | } |
252 | | if (xml_tvb != NULL) { |
253 | | call_dissector_with_data(xml_handle, xml_tvb, pinfo, |
254 | | record_tree, NULL); |
255 | | } |
256 | | #endif |
257 | 171 | break; |
258 | 82 | case NMF_END_RECORD: |
259 | | /* TODO */ |
260 | 82 | break; |
261 | 29 | case NMF_FAULT_RECORD: |
262 | | /* TODO */ |
263 | 29 | break; |
264 | 43 | case NMF_UPGRADE_REQUEST_RECORD: |
265 | 43 | offset = dissect_nmf_record_size(tvb, record_tree, |
266 | 43 | hf_nmf_upgrade_length, |
267 | 43 | offset, &size); |
268 | 43 | if (offset <= 0) { |
269 | 3 | return -1; |
270 | 3 | } |
271 | | |
272 | 40 | proto_tree_add_item_ret_string(record_tree, hf_nmf_upgrade_protocol, |
273 | 40 | tvb, offset, size, ENC_UTF_8, |
274 | 40 | pinfo->pool, &str); |
275 | 40 | offset += size; |
276 | 40 | proto_item_append_text(record_item, ": %s", (const char *)str); |
277 | 40 | break; |
278 | | |
279 | 117 | case NMF_UPGRADE_RESPONSE_RECORD: |
280 | 117 | nmf_info->fnum_upgraded = pinfo->fd->num; |
281 | 117 | break; |
282 | 17 | case NMF_PREAMBLE_ACK_RECORD: |
283 | | /* TODO */ |
284 | 17 | break; |
285 | 47 | case NMF_PREAMBLE_END_RECORD: |
286 | | /* TODO */ |
287 | 47 | break; |
288 | 7.30k | } |
289 | | |
290 | 7.26k | proto_item_append_text(record_item, ", end_offset=0x%x", (unsigned)offset); |
291 | 7.26k | proto_item_set_end(record_item, tvb, offset); |
292 | | |
293 | 7.26k | return offset; |
294 | 7.30k | } |
295 | | |
296 | | static unsigned |
297 | | nmf_get_pdu_len(packet_info *pinfo, tvbuff_t *parent_tvb, int parent_offset, void *_info) |
298 | 7.30k | { |
299 | 7.30k | nmf_conv_info_t *nmf_info = (nmf_conv_info_t *)_info; |
300 | 7.30k | enum nmf_record_type record_type; |
301 | 7.30k | tvbuff_t *tvb = tvb_new_subset_remaining(parent_tvb, parent_offset); |
302 | 7.30k | unsigned offset = 0; |
303 | | |
304 | 7.30k | if (pinfo->fd->num > nmf_info->fnum_negotiated) { |
305 | 0 | unsigned remaining = tvb_captured_length(tvb); |
306 | 0 | unsigned len = 0; |
307 | 0 | unsigned needed = 0; |
308 | |
|
309 | 0 | if (remaining < 4) { |
310 | 0 | return 0; |
311 | 0 | } |
312 | | |
313 | 0 | len = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN); |
314 | |
|
315 | 0 | needed = 4 + len; |
316 | 0 | return needed; |
317 | 0 | } |
318 | | |
319 | 7.30k | if (pinfo->fd->num > nmf_info->fnum_upgraded) { |
320 | 3 | unsigned remaining = tvb_captured_length(tvb); |
321 | 3 | unsigned len = 0; |
322 | 3 | unsigned needed = 0; |
323 | | |
324 | 3 | if (remaining < 5) { |
325 | 0 | return 0; |
326 | 0 | } |
327 | | |
328 | 3 | offset += 3; |
329 | | |
330 | 3 | len = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
331 | | |
332 | 3 | needed = 5 + len; |
333 | 3 | return needed; |
334 | 3 | } |
335 | | |
336 | 7.30k | record_type = (enum nmf_record_type)tvb_get_uint8(tvb, offset); |
337 | 7.30k | offset += 1; |
338 | | |
339 | 7.30k | switch (record_type) { |
340 | 131 | case NMF_VIA_RECORD: |
341 | 306 | case NMF_SIZED_ENVELOPE_RECORD: |
342 | 349 | case NMF_UPGRADE_REQUEST_RECORD: |
343 | 349 | { |
344 | | /* Variable sized record. We must not throw an exception. */ |
345 | 349 | uint64_t size = 0; |
346 | 349 | unsigned len = tvb_get_varint(tvb, offset, |
347 | 349 | tvb_captured_length_remaining(tvb, offset), |
348 | 349 | &size, ENC_VARINT_PROTOBUF); |
349 | | /* [MC-NMF] 2.2.2 The record length can be up to UINT32_MAX, |
350 | | * with an encoded size of 5 bytes. |
351 | | */ |
352 | 349 | if (len == 0) { |
353 | | /* Parsing failed. */ |
354 | 7 | if (tvb_captured_length_remaining(tvb, offset) < 5) { |
355 | | /* Fewer than five bytes, so ask for one more segment. */ |
356 | 5 | return 0; |
357 | 5 | } |
358 | | |
359 | | /* We had at least 5 bytes, so the length is invalid. |
360 | | * Just take the rest of this segment. |
361 | | * The expert info will be handled in the main dissection |
362 | | * routine. */ |
363 | 2 | return tvb_reported_length(tvb); |
364 | 7 | } |
365 | 342 | offset += len; |
366 | 342 | if (/*size > UINT32_MAX || */ ckd_add(&offset, offset, size)) { |
367 | | /* Invalid length or overflow. A size > UINT32_MAX |
368 | | * will always overflow so we don't need to check |
369 | | * unless we want to distinguish the two cases. */ |
370 | 9 | return tvb_reported_length(tvb); |
371 | 9 | } |
372 | 333 | break; |
373 | 342 | } |
374 | 1.25k | case NMF_VERSION_RECORD: |
375 | 1.25k | offset += 2; |
376 | 1.25k | break; |
377 | 339 | case NMF_MODE_RECORD: |
378 | 339 | offset += 1; |
379 | 339 | break; |
380 | 124 | case NMF_KNOWN_ENCODING_RECORD: |
381 | 124 | offset += 1; |
382 | 124 | break; |
383 | 37 | case NMF_EXTENSIBLE_ENCODING_RECORD: |
384 | | /* TODO */ |
385 | 37 | break; |
386 | 148 | case NMF_UNSIZED_ENVELOPE_RECORD: |
387 | | /* TODO */ |
388 | 148 | break; |
389 | 82 | case NMF_END_RECORD: |
390 | | /* TODO */ |
391 | 82 | break; |
392 | 29 | case NMF_FAULT_RECORD: |
393 | | /* TODO */ |
394 | 29 | break; |
395 | 117 | case NMF_UPGRADE_RESPONSE_RECORD: |
396 | 117 | break; |
397 | 17 | case NMF_PREAMBLE_ACK_RECORD: |
398 | | /* TODO */ |
399 | 17 | break; |
400 | 47 | case NMF_PREAMBLE_END_RECORD: |
401 | | /* TODO */ |
402 | 47 | break; |
403 | 7.30k | } |
404 | | |
405 | 7.28k | return offset; |
406 | 7.30k | } |
407 | | |
408 | | static int |
409 | | dissect_nmf_payload(tvbuff_t *tvb, packet_info *pinfo, |
410 | | proto_tree *tree, nmf_conv_info_t *nmf_info) |
411 | 0 | { |
412 | 0 | unsigned offset = 0; |
413 | |
|
414 | 0 | while (tvb_reported_length_remaining(tvb, offset) > 0) { |
415 | 0 | int ret; |
416 | |
|
417 | 0 | ret = dissect_nmf_record(tvb, pinfo, nmf_info, tree, offset); |
418 | 0 | if (ret <= 0) { |
419 | 0 | return -1; |
420 | 0 | } |
421 | 0 | offset += ret; |
422 | 0 | } |
423 | | |
424 | 0 | return offset; |
425 | 0 | } |
426 | | |
427 | | static int |
428 | | dissect_nmf_pdu(tvbuff_t *tvb, packet_info *pinfo, |
429 | | proto_tree *tree, void *_info) |
430 | 7.30k | { |
431 | 7.30k | nmf_conv_info_t *nmf_info = (nmf_conv_info_t *)_info; |
432 | | |
433 | 7.30k | pinfo->fragmented = true; |
434 | | |
435 | 7.30k | if (pinfo->fd->num > nmf_info->fnum_negotiated) { |
436 | 0 | proto_item *item = proto_tree_get_parent(tree); |
437 | 0 | uint32_t len = 0; |
438 | 0 | unsigned offset = 0; |
439 | 0 | tvbuff_t *gssapi_tvb = NULL; |
440 | 0 | tvbuff_t *plain_tvb = NULL, *decr_tvb= NULL; |
441 | 0 | int ver_len; |
442 | 0 | gssapi_encrypt_info_t gssapi_encrypt; |
443 | |
|
444 | 0 | len = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN); |
445 | 0 | proto_tree_add_item(tree, hf_nmf_negotiate_length, |
446 | 0 | tvb, offset, 4, ENC_LITTLE_ENDIAN); |
447 | 0 | offset += 4; |
448 | |
|
449 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "NMF GSSAPI"); |
450 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, |
451 | 0 | "Protected Packet len: %u (0x%x)", |
452 | 0 | (unsigned)len, (unsigned)len); |
453 | 0 | proto_item_append_text(item, ", Protected Packet len: %u (0x%x)", |
454 | 0 | (unsigned)len, (unsigned)len); |
455 | |
|
456 | 0 | gssapi_tvb = tvb_new_subset_length(tvb, offset, len); |
457 | 0 | offset += len; |
458 | | |
459 | | /* Attempt decryption of the GSSAPI wrapped data if possible */ |
460 | 0 | memset(&gssapi_encrypt, 0, sizeof(gssapi_encrypt)); |
461 | 0 | gssapi_encrypt.decrypt_gssapi_tvb=DECRYPT_GSSAPI_NORMAL; |
462 | |
|
463 | 0 | ver_len = call_dissector_with_data(gssapi_wrap_handle, gssapi_tvb, |
464 | 0 | pinfo, tree, &gssapi_encrypt); |
465 | | /* if we could unwrap, do a tvb shuffle */ |
466 | 0 | if (gssapi_encrypt.gssapi_decrypted_tvb) { |
467 | 0 | decr_tvb=gssapi_encrypt.gssapi_decrypted_tvb; |
468 | 0 | } else if (gssapi_encrypt.gssapi_wrap_tvb) { |
469 | 0 | plain_tvb=gssapi_encrypt.gssapi_wrap_tvb; |
470 | 0 | } |
471 | | |
472 | | /* |
473 | | * if we don't have unwrapped data, |
474 | | * see if the wrapping involved encryption of the |
475 | | * data; if not, just use the plaintext data. |
476 | | */ |
477 | 0 | if (!decr_tvb && !plain_tvb) { |
478 | 0 | if(!gssapi_encrypt.gssapi_data_encrypted){ |
479 | 0 | plain_tvb = tvb_new_subset_remaining(gssapi_tvb, ver_len); |
480 | 0 | } |
481 | 0 | } |
482 | |
|
483 | 0 | if (decr_tvb) { |
484 | 0 | proto_tree *enc_tree = NULL; |
485 | 0 | unsigned decr_len = tvb_reported_length(decr_tvb); |
486 | |
|
487 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "NMF GSS-API Privacy (decrypted): "); |
488 | |
|
489 | 0 | if (tree) { |
490 | 0 | enc_tree = proto_tree_add_subtree_format(tree, decr_tvb, 0, -1, |
491 | 0 | ett_nmf_payload, NULL, |
492 | 0 | "GSS-API Encrypted payload (%d byte%s)", |
493 | 0 | decr_len, |
494 | 0 | plurality(decr_len, "", "s")); |
495 | 0 | } |
496 | 0 | dissect_nmf_payload(decr_tvb, pinfo, enc_tree, nmf_info); |
497 | 0 | } else if (plain_tvb) { |
498 | 0 | proto_tree *plain_tree = NULL; |
499 | 0 | unsigned plain_len = tvb_reported_length(plain_tvb); |
500 | |
|
501 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "NMF GSS-API Integrity: "); |
502 | |
|
503 | 0 | if (tree) { |
504 | 0 | plain_tree = proto_tree_add_subtree_format(tree, plain_tvb, 0, -1, |
505 | 0 | ett_nmf_payload, NULL, |
506 | 0 | "GSS-API payload (%d byte%s)", |
507 | 0 | plain_len, |
508 | 0 | plurality(plain_len, "", "s")); |
509 | 0 | } |
510 | |
|
511 | 0 | dissect_nmf_payload(plain_tvb, pinfo, plain_tree, nmf_info); |
512 | 0 | } else { |
513 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "NMF GSS-API Privacy: payload (%d byte%s)", |
514 | 0 | len, plurality(len, "", "s")); |
515 | |
|
516 | 0 | proto_tree_add_format_text(tree, gssapi_tvb, 0, len); |
517 | 0 | } |
518 | 0 | return offset; |
519 | 0 | } |
520 | | |
521 | 7.30k | if (pinfo->fd->num > nmf_info->fnum_upgraded) { |
522 | 3 | proto_item *item = proto_tree_get_parent(tree); |
523 | 3 | unsigned rlen = tvb_reported_length(tvb); |
524 | 3 | uint16_t len = 0; |
525 | 3 | uint8_t type; |
526 | 3 | unsigned offset = 0; |
527 | 3 | tvbuff_t *negotiate_tvb = NULL; |
528 | | |
529 | 3 | col_set_str(pinfo->cinfo, COL_INFO, "NMF Upgrade"); |
530 | | |
531 | 3 | type = tvb_get_uint8(tvb, offset); |
532 | 3 | proto_tree_add_item(tree, hf_nmf_negotiate_type, |
533 | 3 | tvb, offset, 1, ENC_NA); |
534 | 3 | offset += 1; |
535 | 3 | if (type == 0x14) { |
536 | 0 | nmf_info->fnum_negotiated = pinfo->fd->num; |
537 | 0 | } |
538 | | |
539 | 3 | offset += 2; |
540 | | |
541 | 3 | len = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
542 | 3 | proto_tree_add_item(tree, hf_nmf_negotiate_length, |
543 | 3 | tvb, offset, 2, ENC_BIG_ENDIAN); |
544 | 3 | offset += 2; |
545 | | |
546 | 3 | col_add_fstr(pinfo->cinfo, COL_INFO, |
547 | 3 | "Upgraded Packet rlen: %u (0x%x)", |
548 | 3 | (unsigned)rlen, (unsigned)rlen); |
549 | 3 | proto_item_append_text(item, ", Upgraded Packet rlen: %u (0x%x) len: %u (0x%x) type: 0x%02x", |
550 | 3 | (unsigned)rlen, (unsigned)rlen, |
551 | 3 | (unsigned)len, (unsigned)len, |
552 | 3 | (unsigned)type); |
553 | 3 | negotiate_tvb = tvb_new_subset_length(tvb, offset, len); |
554 | | |
555 | 3 | call_dissector(gssapi_handle, negotiate_tvb, pinfo, tree); |
556 | 3 | offset += len; |
557 | 3 | return offset; |
558 | 3 | } |
559 | | |
560 | 7.30k | return dissect_nmf_record(tvb, pinfo, nmf_info, tree, 0); |
561 | 7.30k | } |
562 | | |
563 | | static int |
564 | | dissect_nmf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, _U_ void *_unused) |
565 | 67 | { |
566 | 67 | conversation_t *conv = NULL; |
567 | 67 | nmf_conv_info_t *nmf_info = NULL; |
568 | 67 | proto_tree *tree = NULL; |
569 | 67 | proto_item *item = NULL; |
570 | | |
571 | 67 | conv = find_or_create_conversation(pinfo); |
572 | 67 | nmf_info = (nmf_conv_info_t *)conversation_get_proto_data(conv, |
573 | 67 | proto_nmf); |
574 | 67 | if (nmf_info == NULL) { |
575 | 53 | nmf_info = wmem_new0(wmem_file_scope(), nmf_conv_info_t); |
576 | 53 | nmf_info->fnum_upgraded = 0xffffffff; |
577 | 53 | nmf_info->fnum_negotiated = 0xffffffff; |
578 | 53 | conversation_add_proto_data(conv, proto_nmf, nmf_info); |
579 | 53 | } |
580 | | |
581 | 67 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "NMF"); |
582 | 67 | col_set_str(pinfo->cinfo, COL_INFO, "NMF..."); |
583 | | |
584 | 67 | if (parent_tree != NULL) { |
585 | 67 | item = proto_tree_add_item(parent_tree, proto_nmf, tvb, 0, -1, ENC_NA); |
586 | 67 | tree = proto_item_add_subtree(item, ett_nmf); |
587 | 67 | } |
588 | | |
589 | 67 | tcp_dissect_pdus(tvb, pinfo, tree, nmf_reassemble, |
590 | 67 | 1, /* fixed_length */ |
591 | 67 | nmf_get_pdu_len, |
592 | 67 | dissect_nmf_pdu, |
593 | 67 | nmf_info); |
594 | 67 | return tvb_captured_length(tvb); |
595 | 67 | } |
596 | | |
597 | | void proto_register_nmf(void) |
598 | 16 | { |
599 | 16 | static int *ett[] = { |
600 | 16 | &ett_nmf, |
601 | 16 | &ett_nmf_payload, |
602 | 16 | }; |
603 | 16 | static hf_register_info hf[] = { |
604 | 16 | { &hf_nmf_record, |
605 | 16 | { "Record", "nmf.record", |
606 | 16 | FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
607 | 16 | { &hf_nmf_record_type, |
608 | 16 | { "Type", "nmf.type", |
609 | 16 | FT_UINT8, BASE_DEC, VALS(record_types), 0, NULL, HFILL }}, |
610 | 16 | { &hf_nmf_version_major, |
611 | 16 | { "Version Major", "nmf.version.major", |
612 | 16 | FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }}, |
613 | 16 | { &hf_nmf_version_minor, |
614 | 16 | { "Version minor", "nmf.version.minor", |
615 | 16 | FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }}, |
616 | 16 | { &hf_nmf_mode_value, |
617 | 16 | { "Mode", "nmf.mode.value", |
618 | 16 | FT_UINT8, BASE_DEC, VALS(mode_values), 0, NULL, HFILL }}, |
619 | 16 | { &hf_nmf_via_length, |
620 | 16 | { "Length", "nmf.via.length", |
621 | 16 | FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
622 | 16 | { &hf_nmf_via_value, |
623 | 16 | { "URI", "nmf.via.uri", |
624 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, "Via URI", HFILL }}, |
625 | 16 | { &hf_nmf_known_mode_value, |
626 | 16 | { "Mode", "nmf.known_mode.value", |
627 | 16 | FT_UINT8, BASE_DEC, VALS(known_mode_values), 0, NULL, HFILL }}, |
628 | 16 | { &hf_nmf_sized_envelope_length, |
629 | 16 | { "Length", "nmf.sized_envelope.length", |
630 | 16 | FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
631 | 16 | { &hf_nmf_upgrade_length, |
632 | 16 | { "Length", "nmf.upgrade.length", |
633 | 16 | FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
634 | 16 | { &hf_nmf_upgrade_protocol, |
635 | 16 | { "Upgrade Protocol", "nmf.upgrade.protocol", |
636 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
637 | 16 | { &hf_nmf_negotiate_type, |
638 | 16 | { "Negotiate Type", "nmf.negotiate.type", |
639 | 16 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
640 | 16 | { &hf_nmf_negotiate_length, |
641 | 16 | { "Negotiate Length", "nmf.negotiate.length", |
642 | 16 | FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
643 | 16 | { &hf_nmf_protect_length, |
644 | 16 | { "Protect Length", "nmf.protect.length", |
645 | 16 | FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
646 | 16 | }; |
647 | 16 | module_t *nmf_module = NULL; |
648 | | |
649 | 16 | proto_nmf = proto_register_protocol("NMF (.NET Message Framing Protocol)", |
650 | 16 | "NMF", "nmf"); |
651 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
652 | 16 | proto_register_field_array(proto_nmf, hf, array_length(hf)); |
653 | | |
654 | 16 | nmf_module = prefs_register_protocol(proto_nmf, NULL); |
655 | | |
656 | 16 | expert_module_t *expert_nmf; |
657 | 16 | static ei_register_info ei[] = { |
658 | 16 | { &ei_nmf_bad_record_size, |
659 | 16 | { "nmf.bad_record_size", PI_MALFORMED, PI_WARN, "Invalid record size varint", EXPFILL }}, |
660 | 16 | }; |
661 | | |
662 | 16 | expert_nmf = expert_register_protocol(proto_nmf); |
663 | 16 | expert_register_field_array(expert_nmf, ei, array_length(ei)); |
664 | | |
665 | 16 | prefs_register_bool_preference(nmf_module, |
666 | 16 | "reassemble_nmf", |
667 | 16 | "Reassemble NMF fragments", |
668 | 16 | "Whether the NMF dissector should reassemble fragmented payloads", |
669 | 16 | &nmf_reassemble); |
670 | 16 | } |
671 | | |
672 | | |
673 | | void |
674 | | proto_reg_handoff_nmf(void) |
675 | 16 | { |
676 | 16 | dissector_handle_t nmf_handle; |
677 | | |
678 | 16 | nmf_handle = create_dissector_handle(dissect_nmf, proto_nmf); |
679 | 16 | dissector_add_uint_with_preference("tcp.port", NMF_PORT, nmf_handle); |
680 | | |
681 | 16 | gssapi_handle = find_dissector_add_dependency("gssapi", proto_nmf); |
682 | 16 | gssapi_wrap_handle = find_dissector_add_dependency("gssapi_verf", proto_nmf); |
683 | | |
684 | 16 | xml_handle = find_dissector_add_dependency("xml", proto_nmf); |
685 | 16 | } |