2 * Dissect the NEGOEX security protocol
3 * as described here: https://tools.ietf.org/html/draft-zhu-negoex-04
4 * Copyright 2012 Richard Sharpe <realrichardsharpe@gmail.com>
5 * Routines for SPNEGO Extended Negotiation Security Mechanism
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/exceptions.h>
18 #include <epan/show_exception.h>
20 #include "packet-gssapi.h"
22 void proto_register_negoex(void);
23 void proto_reg_handoff_negoex(void);
25 static int proto_negoex
;
26 static int hf_negoex_sig
;
27 static int hf_negoex_message_type
;
28 static int hf_negoex_sequence_num
;
29 static int hf_negoex_header_len
;
30 static int hf_negoex_message_len
;
31 static int hf_negoex_conversation_id
;
32 static int hf_negoex_random
;
33 static int hf_negoex_proto_version
;
34 static int hf_negoex_authscheme
;
35 static int hf_negoex_authscheme_vector_offset
;
36 static int hf_negoex_authscheme_vector_count
;
37 static int hf_negoex_authscheme_vector_pad
;
38 static int hf_negoex_extension
;
39 static int hf_negoex_extension_vector_offset
;
40 static int hf_negoex_extension_vector_count
;
41 static int hf_negoex_extension_vector_pad
;
42 static int hf_negoex_exchange_vector_offset
;
43 static int hf_negoex_exchange_vector_count
;
44 static int hf_negoex_exchange_vector_pad
;
45 static int hf_negoex_exchange
;
46 static int hf_negoex_checksum_scheme
;
47 static int hf_negoex_checksum_type
;
48 static int hf_negoex_checksum_vector_offset
;
49 static int hf_negoex_checksum_vector_count
;
50 static int hf_negoex_checksum_vector_pad
;
51 static int hf_negoex_checksum
;
52 static int hf_negoex_errorcode
;
53 static int hf_negoex_data
;
55 static int ett_negoex
;
56 static int ett_negoex_msg
;
57 static int ett_negoex_hdr
;
58 static int ett_negoex_authscheme_vector
;
59 static int ett_negoex_extension_vector
;
60 static int ett_negoex_exchange
;
61 static int ett_negoex_checksum
;
62 static int ett_negoex_checksum_vector
;
63 static int ett_negoex_byte_vector
;
65 static dissector_handle_t negoex_handle
;
67 /* If you add more message types, add them in sequence and update MAX_MSG */
68 #define MESSAGE_TYPE_INITIATOR_NEGO 0
69 #define MESSAGE_TYPE_ACCEPTOR_NEGO 1
70 #define MESSAGE_TYPE_INITIATOR_META_DATA 2
71 #define MESSAGE_TYPE_ACCEPTOR_META_DATA 3
72 #define MESSAGE_TYPE_CHALLENGE 4
73 #define MESSAGE_TYPE_AP_REQUEST 5
74 #define MESSAGE_TYPE_VERIFY 6
75 #define MESSAGE_TYPE_ALERT 7
76 #define MESSAGE_TYPE_MAX_MSG MESSAGE_TYPE_ALERT
78 static const value_string negoex_message_types
[] = {
79 {MESSAGE_TYPE_INITIATOR_NEGO
, "INITATOR_NEGO"},
80 {MESSAGE_TYPE_ACCEPTOR_NEGO
, "ACCEPTOR_NEGO"},
81 {MESSAGE_TYPE_INITIATOR_META_DATA
, "INITIATOR_META_DATA"},
82 {MESSAGE_TYPE_ACCEPTOR_META_DATA
, "ACCEPTOR_META_DATA"},
83 {MESSAGE_TYPE_CHALLENGE
, "CHALLENGE"},
84 {MESSAGE_TYPE_AP_REQUEST
, "AP_REQUEST"},
85 {MESSAGE_TYPE_VERIFY
, "VERIFY"},
86 {MESSAGE_TYPE_ALERT
, "ALERT"},
90 static const value_string checksum_schemes
[] = {
96 static const value_string alert_types
[] = {
97 {1, "ALERT_TYPE_PULSE"},
101 static const value_string alert_reasons
[] = {
102 {1, "ALERT_VERIFY_NO_KEY"},
108 dissect_negoex_alert_message(tvbuff_t
*tvb
,
109 packet_info
*pinfo _U_
,
118 proto_tree_add_item(tree
, hf_negoex_authscheme
, tvb
, offset
, 16, ENC_LITTLE_ENDIAN
);
121 /* ErrorCode, an NTSTATUS :-) */
122 proto_tree_add_item(tree
, hf_negoex_errorcode
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
126 proto_tree_add_bytes_format(tree
, hf_negoex_data
, tvb
, offset
, -1, NULL
,
127 "The rest of the alert message");
132 dissect_negoex_verify_message(tvbuff_t
*tvb
,
133 packet_info
*pinfo _U_
,
138 uint32_t checksum_vector_offset
;
139 uint32_t checksum_vector_count
;
140 proto_tree
*checksum
;
141 proto_tree
*checksum_vector
;
146 proto_tree_add_item(tree
, hf_negoex_authscheme
, tvb
, offset
, 16, ENC_LITTLE_ENDIAN
);
150 checksum
= proto_tree_add_subtree(tree
, tvb
, offset
, 20, ett_negoex_checksum
, NULL
, "Checksum");
153 proto_tree_add_item(checksum
, hf_negoex_header_len
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
157 proto_tree_add_item(checksum
, hf_negoex_checksum_scheme
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
161 proto_tree_add_item(checksum
, hf_negoex_checksum_type
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
164 /* Checksum Byte Vector */
165 checksum_vector_offset
= tvb_get_letohl(tvb
, offset
);
166 checksum_vector_count
= tvb_get_letohs(tvb
, offset
+ 4);
168 checksum_vector
= proto_tree_add_subtree_format(checksum
, tvb
, offset
, 8,
169 ett_negoex_checksum_vector
, NULL
, "Checksum Vector: %u at %u",
170 checksum_vector_count
,
171 checksum_vector_offset
);
173 proto_tree_add_item(checksum_vector
, hf_negoex_checksum_vector_offset
, tvb
,
174 offset
, 4, ENC_LITTLE_ENDIAN
);
177 proto_tree_add_item(checksum_vector
, hf_negoex_checksum_vector_count
, tvb
,
178 offset
, 2, ENC_LITTLE_ENDIAN
);
181 proto_tree_add_item(checksum_vector
, hf_negoex_checksum_vector_pad
, tvb
,
185 proto_tree_add_item(checksum_vector
, hf_negoex_checksum
, tvb
,
186 checksum_vector_offset
, checksum_vector_count
, ENC_NA
);
191 dissect_negoex_exchange_message(tvbuff_t
*tvb
,
192 packet_info
*pinfo _U_
,
197 uint32_t exchange_vector_offset
;
198 uint32_t exchange_vector_count
;
199 proto_tree
*exchange_vector
;
204 proto_tree_add_item(tree
, hf_negoex_authscheme
, tvb
, offset
, 16, ENC_LITTLE_ENDIAN
);
207 /* Exchange Byte Vector */
208 exchange_vector_offset
= tvb_get_letohl(tvb
, offset
);
209 exchange_vector_count
= tvb_get_letohs(tvb
, offset
+ 4);
211 exchange_vector
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 8,
212 ett_negoex_exchange
, NULL
, "Exchange: %u bytes at %u",
213 exchange_vector_count
, exchange_vector_offset
);
215 proto_tree_add_item(exchange_vector
, hf_negoex_exchange_vector_offset
, tvb
,
216 offset
, 4, ENC_LITTLE_ENDIAN
);
219 proto_tree_add_item(exchange_vector
, hf_negoex_exchange_vector_count
, tvb
,
220 offset
, 2, ENC_LITTLE_ENDIAN
);
223 proto_tree_add_item(exchange_vector
, hf_negoex_exchange_vector_pad
, tvb
,
227 proto_tree_add_item(exchange_vector
, hf_negoex_exchange
, tvb
,
228 exchange_vector_offset
, exchange_vector_count
, ENC_NA
);
232 * In each of the subdissectors we are handed the whole message, but the
233 * header is already dissected. The offset tells us where in the buffer the
234 * actual data starts. This is a bit redundant, but it allows for changes
235 * to the header structure ...
237 * Eventually we want to treat the header and body differently perhaps.
240 dissect_negoex_nego_message(tvbuff_t
*tvb
,
241 packet_info
*pinfo _U_
,
245 volatile uint32_t offset
;
246 uint32_t authscheme_vector_offset
;
247 uint16_t authscheme_vector_count
;
248 uint32_t extension_vector_offset
;
249 uint32_t extension_vector_count
;
250 proto_tree
*authscheme_vector
;
251 proto_tree
*extension_vector
;
257 /* The Random field */
258 proto_tree_add_item(tree
, hf_negoex_random
, tvb
, offset
, 32, ENC_ASCII
);
261 /* Protocol version */
262 proto_tree_add_item(tree
, hf_negoex_proto_version
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
265 /* AuthScheme offset and count */
266 authscheme_vector_offset
= tvb_get_letohl(tvb
, offset
);
267 authscheme_vector_count
= tvb_get_letohs(tvb
, offset
+ 4);
269 authscheme_vector
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 8,
270 ett_negoex_authscheme_vector
, NULL
, "AuthSchemes: %u at %u",
271 authscheme_vector_count
, authscheme_vector_offset
);
272 proto_tree_add_item(authscheme_vector
, hf_negoex_authscheme_vector_offset
,
273 tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
276 proto_tree_add_item(authscheme_vector
, hf_negoex_authscheme_vector_count
,
277 tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
280 proto_tree_add_item(authscheme_vector
, hf_negoex_authscheme_vector_pad
,
281 tvb
, offset
, 2, ENC_NA
);
284 /* Now, add the various items */
285 for (i
= 0; i
< authscheme_vector_count
; i
++) {
286 proto_tree_add_item(authscheme_vector
, hf_negoex_authscheme
, tvb
,
287 authscheme_vector_offset
+ i
* 16, 16, ENC_LITTLE_ENDIAN
);
290 extension_vector_offset
= tvb_get_letohl(tvb
, offset
);
291 extension_vector_count
= tvb_get_letohs(tvb
, offset
+ 4);
293 extension_vector
= proto_tree_add_subtree_format(tree
, tvb
, offset
, 8,
294 ett_negoex_extension_vector
, NULL
, "Extensions: %u at %u",
295 extension_vector_count
, extension_vector_count
);
297 proto_tree_add_item(extension_vector
, hf_negoex_extension_vector_offset
,
298 tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
301 proto_tree_add_item(extension_vector
, hf_negoex_extension_vector_count
,
302 tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
305 proto_tree_add_item(extension_vector
, hf_negoex_extension_vector_pad
,
306 tvb
, offset
, 2, ENC_NA
);
309 for (i
= 0; i
< extension_vector_count
; i
++) {
310 uint32_t byte_vector_offset
, byte_vector_count
;
314 * Dissect these things ... they consist of a byte vector, so we
315 * add a subtree and point to the relevant bytes
317 byte_vector_offset
= tvb_get_letohl(tvb
, offset
);
318 byte_vector_count
= tvb_get_letohs(tvb
, offset
+ 4);
320 bv_tree
= proto_tree_add_subtree_format(extension_vector
, tvb
,
321 extension_vector_offset
+ i
* 8, 8,
322 ett_negoex_byte_vector
, NULL
, "Extension: %u bytes at %u",
323 byte_vector_count
, byte_vector_offset
);
325 proto_tree_add_item(bv_tree
, hf_negoex_extension
, tvb
,
326 byte_vector_offset
, byte_vector_count
, ENC_NA
);
335 dissect_negoex(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
337 volatile uint32_t offset
;
338 proto_tree
* volatile negoex_tree
;
341 uint32_t payload_len
;
342 uint32_t message_len
;
343 uint32_t message_type
;
350 payload_len
= tvb_reported_length(tvb
);
352 /* Set up the initial NEGOEX payload */
354 tf
= proto_tree_add_item(tree
, proto_negoex
, tvb
, offset
, -1, ENC_NA
);
355 negoex_tree
= proto_item_add_subtree(tf
, ett_negoex
);
359 * There can be multiple negoex messages, each with a header with a length.
360 * However, the payload might not have been reassembled ...
363 while (offset
< payload_len
&& !done
) {
364 proto_tree
*negoex_msg_tree
;
365 proto_tree
*negoex_hdr_tree
;
368 uint32_t start_offset
;
370 start_offset
= offset
;
373 /* Message type, it is after the signature */
374 message_type
= tvb_get_letohl(tvb
, offset
+ 8);
376 /* Add the message type tree ... set its length below */
377 negoex_msg_tree
= proto_tree_add_subtree_format(negoex_tree
, tvb
, offset
, -1,
378 ett_negoex_msg
, &msg
, "NEGOEX %s",
379 val_to_str_const(message_type
,
380 negoex_message_types
,
381 "Unknown NEGOEX message type"));
383 /* Add a subtree for the header */
384 negoex_hdr_tree
= proto_tree_add_subtree(negoex_msg_tree
, tvb
, offset
, 40, ett_negoex_hdr
, NULL
, "Header");
386 /* Signature, NEGOEXTS */
387 proto_tree_add_item(negoex_hdr_tree
, hf_negoex_sig
,
388 tvb
, offset
, 8, ENC_ASCII
| ENC_NA
);
391 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ",
392 val_to_str_const(message_type
,
393 negoex_message_types
,
394 "Unknown NEGOEX message type"));
395 proto_tree_add_uint(negoex_hdr_tree
, hf_negoex_message_type
,
396 tvb
, offset
, 4, message_type
);
399 * If this is an unknown message type, we have to punt because anything
400 * following cannot be handled
402 if (message_type
> MESSAGE_TYPE_MAX_MSG
) {
403 offset
= payload_len
; /* Can't do any more */
409 /* Sequence Number */
410 proto_tree_add_item(negoex_hdr_tree
, hf_negoex_sequence_num
,
411 tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
415 header_len
= tvb_get_letohl(tvb
, offset
);
416 proto_tree_add_uint(negoex_hdr_tree
, hf_negoex_header_len
,
417 tvb
, offset
, 4, header_len
);
421 message_len
= tvb_get_letohl(tvb
, offset
);
422 proto_tree_add_uint(negoex_hdr_tree
, hf_negoex_message_len
,
423 tvb
, offset
, 4, message_len
);
426 /* Set the message len so the tree item has correct len */
427 proto_item_set_len(msg
, message_len
);
429 /* Conversation ID */
430 proto_tree_add_item(negoex_hdr_tree
, hf_negoex_conversation_id
,
431 tvb
, offset
, 16, ENC_LITTLE_ENDIAN
);
435 * Construct a new TVB covering just this message and pass to the
438 msg_tvb
= tvb_new_subset_length_caplen(tvb
,
440 MIN(message_len
, tvb_captured_length(tvb
)),
443 switch (message_type
) {
444 case MESSAGE_TYPE_INITIATOR_NEGO
:
445 case MESSAGE_TYPE_ACCEPTOR_NEGO
:
446 dissect_negoex_nego_message(msg_tvb
,
449 offset
- start_offset
);
452 case MESSAGE_TYPE_INITIATOR_META_DATA
:
453 case MESSAGE_TYPE_ACCEPTOR_META_DATA
:
454 case MESSAGE_TYPE_CHALLENGE
:
455 case MESSAGE_TYPE_AP_REQUEST
:
456 dissect_negoex_exchange_message(msg_tvb
,
459 offset
- start_offset
);
462 case MESSAGE_TYPE_VERIFY
:
463 dissect_negoex_verify_message(msg_tvb
,
466 offset
- start_offset
);
469 case MESSAGE_TYPE_ALERT
:
470 dissect_negoex_alert_message(msg_tvb
,
473 offset
- start_offset
);
477 proto_tree_add_bytes_format(negoex_msg_tree
, hf_negoex_data
, tvb
, offset
, message_len
- 40, NULL
,
478 "The rest of the message");
481 offset
= start_offset
+ message_len
;
483 /* We cannot branch out of the TRY block, but we can branch here */
487 } CATCH_NONFATAL_ERRORS
{
489 show_exception(tvb
, pinfo
, tree
, EXCEPT_CODE
, GET_MESSAGE
);
493 return tvb_captured_length(tvb
);
497 proto_register_negoex(void)
500 static hf_register_info hf
[] = {
502 { "Signature", "negoex.message.sig", FT_STRING
, BASE_NONE
,
503 NULL
, 0x0, NULL
, HFILL
}},
504 { &hf_negoex_message_type
,
505 { "MessageType", "negoex.message.type", FT_UINT32
, BASE_HEX
,
506 VALS(negoex_message_types
), 0x00, NULL
, HFILL
}},
507 { &hf_negoex_sequence_num
,
508 { "SequenceNum", "negoex.message.seq_num", FT_UINT32
, BASE_DEC
,
509 NULL
, 0x0, NULL
, HFILL
}},
510 { &hf_negoex_header_len
,
511 { "cbHeaderLength", "negoex.header.len", FT_UINT32
, BASE_DEC
,
512 NULL
, 0x0, NULL
, HFILL
}},
513 { &hf_negoex_message_len
,
514 { "cbMessageLength", "negoex.message.len", FT_UINT32
, BASE_DEC
,
515 NULL
, 0x0, NULL
, HFILL
}},
516 { &hf_negoex_conversation_id
,
517 { "ConversationID", "negoex.message.conv_id", FT_GUID
, BASE_NONE
,
518 NULL
, 0x0, NULL
, HFILL
}},
520 { "Random", "negoex.message.random", FT_BYTES
, BASE_NONE
,
521 NULL
, 0x0, "Random data", HFILL
}},
522 { &hf_negoex_proto_version
,
523 { "ProtocolVersion", "negoex.proto_version", FT_UINT64
, BASE_DEC
,
524 NULL
, 0x0, NULL
, HFILL
}},
525 { &hf_negoex_authscheme
,
526 { "AuthScheme", "negoex.auth_scheme", FT_GUID
, BASE_NONE
,
527 NULL
, 0x0, NULL
, HFILL
}},
528 { &hf_negoex_authscheme_vector_offset
,
529 { "AuthSchemeArrayOffset", "negoex.auth_scheme_array_offset", FT_UINT32
,
530 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
531 { &hf_negoex_authscheme_vector_count
,
532 { "AuthSchemeCount", "negoex.auth_scheme_array_count", FT_UINT16
,
533 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
534 { &hf_negoex_authscheme_vector_pad
,
535 { "AuthSchemePad", "negoex.auth_scheme_array_pad", FT_BYTES
,
536 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
537 { &hf_negoex_extension
,
538 { "Extension", "negoex.extension", FT_BYTES
, BASE_NONE
,
539 NULL
, 0x0, "Extension data", HFILL
}},
540 { &hf_negoex_extension_vector_offset
,
541 { "ExtensionArrayOffset", "negoex.extension_array_offset", FT_UINT32
,
542 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
543 { &hf_negoex_extension_vector_count
,
544 { "ExtensionCount", "negoex.extension_array_count", FT_UINT16
,
545 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
546 { &hf_negoex_extension_vector_pad
,
547 { "ExtensionPad", "negoex.extension_pad", FT_BYTES
,
548 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
549 { &hf_negoex_exchange_vector_offset
,
550 { "ExchangeOffset", "negoex.exchange_vec_offset", FT_UINT32
, BASE_DEC
,
551 NULL
, 0x0, NULL
, HFILL
}},
552 { &hf_negoex_exchange_vector_count
,
553 { "ExchangeByteCount", "negoex.exchange_vec_byte_count", FT_UINT16
,
554 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
555 { &hf_negoex_exchange_vector_pad
,
556 { "ExchangePad", "negoex.exchange_vec_pad", FT_BYTES
, BASE_NONE
,
557 NULL
, 0x0, NULL
, HFILL
}},
558 { &hf_negoex_exchange
,
559 { "Exchange Bytes", "negoex.exchange", FT_BYTES
, BASE_NONE
,
560 NULL
, 0x0, NULL
, HFILL
}},
561 { &hf_negoex_checksum_scheme
,
562 { "ChecksumScheme", "negoex.checksum_scheme", FT_UINT32
, BASE_DEC
,
563 VALS(checksum_schemes
), 0x0, NULL
, HFILL
}},
564 { &hf_negoex_checksum_vector_offset
,
565 { "ChecksumOffset", "negoex.checksum_vec_offset", FT_UINT32
, BASE_DEC
,
566 NULL
, 0x0, NULL
, HFILL
}},
567 { &hf_negoex_checksum_vector_count
,
568 { "ChecksumCount", "negoex.checksum_vec_count", FT_UINT16
, BASE_DEC
,
569 NULL
, 0x0, NULL
, HFILL
}},
570 { &hf_negoex_checksum_vector_pad
,
571 { "ChecksumPad", "negoex.checksum_pad", FT_BYTES
, BASE_NONE
,
572 NULL
, 0x0, NULL
, HFILL
}},
573 { &hf_negoex_checksum_type
,
574 { "ChecksumType", "negoex.checksum_type", FT_UINT32
, BASE_DEC
,
575 NULL
, 0x0, NULL
, HFILL
}},
576 { &hf_negoex_checksum
,
577 { "Checksum", "negoex.checksum", FT_BYTES
, BASE_NONE
,
578 NULL
, 0x0, NULL
, HFILL
}},
579 { &hf_negoex_errorcode
,
580 { "ErrorCode", "negoex.errorcode", FT_UINT32
, BASE_HEX
,
581 NULL
, 0x0, NULL
, HFILL
}},
583 { "Data", "negoex.data", FT_BYTES
, BASE_NONE
,
584 NULL
, 0x0, NULL
, HFILL
}},
587 static int *ett
[] = {
591 &ett_negoex_authscheme_vector
,
592 &ett_negoex_extension_vector
,
593 &ett_negoex_exchange
,
594 &ett_negoex_checksum
,
595 &ett_negoex_checksum_vector
,
596 &ett_negoex_byte_vector
,
598 /*module_t *negoex_module = NULL; */
600 proto_negoex
= proto_register_protocol (
601 "SPNEGO Extended Negotiation Security Mechanism", /* name */
602 "NEGOEX", /* short name */
603 "negoex" /* abbrev */
605 proto_register_field_array(proto_negoex
, hf
, array_length(hf
));
606 proto_register_subtree_array(ett
, array_length(ett
));
608 /* negoex_module = prefs_register_protocol(proto_negoex, NULL);*/
610 negoex_handle
= register_dissector("negoex", dissect_negoex
, proto_negoex
);
614 proto_reg_handoff_negoex(void)
617 /* Register protocol with the GSS-API module */
618 gssapi_init_oid("1.3.6.1.4.1.311.2.2.30", proto_negoex
, ett_negoex
,
620 "NEGOEX - SPNEGO Extended Negotiation Security Mechanism");
625 * Editor modelines - https://www.wireshark.org/tools/modelines.html
630 * indent-tabs-mode: nil
633 * vi: set shiftwidth=2 tabstop=8 expandtab:
634 * :indentSize=2:tabSize=8:noTabs=true: