summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-ntlmssp.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/packet-ntlmssp.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-ntlmssp.c')
-rw-r--r--epan/dissectors/packet-ntlmssp.c3697
1 files changed, 3697 insertions, 0 deletions
diff --git a/epan/dissectors/packet-ntlmssp.c b/epan/dissectors/packet-ntlmssp.c
new file mode 100644
index 00000000..85ec0e9a
--- /dev/null
+++ b/epan/dissectors/packet-ntlmssp.c
@@ -0,0 +1,3697 @@
+/* packet-ntlmssp.c
+ * Add-on for better NTLM v1/v2 handling
+ * Copyright 2009, 2012 Matthieu Patou <mat@matws.net>
+ * Routines for NTLM Secure Service Provider
+ * Devin Heitmueller <dheitmueller@netilla.com>
+ * Copyright 2003, Tim Potter <tpot@samba.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+/* Just set me to activate debug #define DEBUG_NTLMSSP */
+#include "config.h"
+#ifdef DEBUG_NTLMSSP
+#include <stdio.h>
+#endif
+#include <string.h>
+
+#include <epan/packet.h>
+#include <epan/exceptions.h>
+#include <epan/asn1.h>
+#include <epan/prefs.h>
+#include <epan/tap.h>
+#include <epan/expert.h>
+#include <epan/show_exception.h>
+#include <epan/proto_data.h>
+
+#include <wsutil/wsgcrypt.h>
+#include <wsutil/crc32.h>
+#include <wsutil/str_util.h>
+
+#include "packet-windows-common.h"
+#include "packet-kerberos.h"
+#include "packet-dcerpc.h"
+#include "packet-gssapi.h"
+
+#include "read_keytab_file.h"
+
+#include "packet-ntlmssp.h"
+
+/*
+ * See
+ *
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/
+ *
+ * for Microsoft's MS-NLMP, NT LAN Manager (NTLM) Authentication Protocol
+ * Specification.
+ *
+ * See also
+ *
+ * http://davenport.sourceforge.net/ntlm.html
+ *
+ * which indicates that, in practice, some fields specified by MS-NLMP
+ * may be absent; this has been seen in some captures.
+ */
+
+void proto_register_ntlmssp(void);
+void proto_reg_handoff_ntlmssp(void);
+
+static int ntlmssp_tap = -1;
+
+#define CLIENT_SIGN_TEXT "session key to client-to-server signing key magic constant"
+#define CLIENT_SEAL_TEXT "session key to client-to-server sealing key magic constant"
+#define SERVER_SIGN_TEXT "session key to server-to-client signing key magic constant"
+#define SERVER_SEAL_TEXT "session key to server-to-client sealing key magic constant"
+
+static const value_string ntlmssp_message_types[] = {
+ { NTLMSSP_NEGOTIATE, "NTLMSSP_NEGOTIATE" },
+ { NTLMSSP_CHALLENGE, "NTLMSSP_CHALLENGE" },
+ { NTLMSSP_AUTH, "NTLMSSP_AUTH" },
+ { NTLMSSP_UNKNOWN, "NTLMSSP_UNKNOWN" },
+ { 0, NULL }
+};
+
+#define NTLMSSP_EK_IS_NT4HASH(ek) \
+ (ek->fd_num == -1 && ek->keytype == 23 && ek->keylength == NTLMSSP_KEY_LEN)
+
+static const unsigned char gbl_zeros[24] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+static GHashTable* hash_packet = NULL;
+
+/*
+ * NTLMSSP negotiation flags
+ * Taken from Samba
+ *
+ * See also the davenport.sourceforge.net document cited above,
+ * although that document says that:
+ *
+ * 0x00010000 is "Target Type Domain";
+ * 0x00020000 is "Target Type Server"
+ * 0x00040000 is "Target Type Share";
+ *
+ * and that 0x00100000, 0x00200000, and 0x00400000 are
+ * "Request Init Response", "Request Accept Response", and
+ * "Request Non-NT Session Key", rather than those values shifted
+ * right one having those interpretations.
+ *
+ * UPDATE: Further information obtained from [MS-NLMP] 2.2.2.5, added in comments
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
+ */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 // A
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002 // B
+#define NTLMSSP_REQUEST_TARGET 0x00000004 // C
+#define NTLMSSP_UNUSED_00000008 0x00000008 // r10
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 // D
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 // E
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 // F
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 // G, "requests LAN Manager (LM) session key computation", aka NTLMv1
+#define NTLMSSP_UNUSED_00000100 0x00000100 // r9
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 // H, "requests usage of the NTLM v1 session security protocol"
+#define NTLMSSP_UNUSED_00000400 0x00000400 // r8
+#define NTLMSSP_NEGOTIATE_ANONYMOUS 0x00000800 // J
+#define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 0x00001000 // K
+#define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 0x00002000 // L
+#define NTLMSSP_UNUSED_00004000 0x00004000 // r7
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 // M
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 // N
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 // O
+#define NTLMSSP_UNUSED_00040000 0x00040000 // r6
+#define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000 // P, "requests usage of the NTLM v2 session security. NTLM v2 session security is a misnomer because it is not NTLM v2. It is NTLM v1 using the extended session security that is also in NTLM v2"
+#define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 // Q
+#define NTLMSSP_UNUSED_00200000 0x00200000 // r5
+#define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 // R, "requests the usage of the LMOWF"
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 // S
+#define NTLMSSP_UNUSED_01000000 0x01000000 // r4
+#define NTLMSSP_NEGOTIATE_VERSION 0x02000000 // T
+#define NTLMSSP_UNUSED_04000000 0x04000000 // r3
+#define NTLMSSP_UNUSED_08000000 0x08000000 // r2
+#define NTLMSSP_UNUSED_10000000 0x10000000 // r1
+#define NTLMSSP_NEGOTIATE_128 0x20000000 // U
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 // V
+#define NTLMSSP_NEGOTIATE_56 0x80000000 // W
+
+static int proto_ntlmssp = -1;
+static int hf_ntlmssp_auth = -1;
+static int hf_ntlmssp_message_type = -1;
+static int hf_ntlmssp_negotiate_flags = -1;
+static int hf_ntlmssp_negotiate_flags_01 = -1;
+static int hf_ntlmssp_negotiate_flags_02 = -1;
+static int hf_ntlmssp_negotiate_flags_04 = -1;
+static int hf_ntlmssp_negotiate_flags_08 = -1;
+static int hf_ntlmssp_negotiate_flags_10 = -1;
+static int hf_ntlmssp_negotiate_flags_20 = -1;
+static int hf_ntlmssp_negotiate_flags_40 = -1;
+static int hf_ntlmssp_negotiate_flags_80 = -1;
+static int hf_ntlmssp_negotiate_flags_100 = -1;
+static int hf_ntlmssp_negotiate_flags_200 = -1;
+static int hf_ntlmssp_negotiate_flags_400 = -1;
+static int hf_ntlmssp_negotiate_flags_800 = -1;
+static int hf_ntlmssp_negotiate_flags_1000 = -1;
+static int hf_ntlmssp_negotiate_flags_2000 = -1;
+static int hf_ntlmssp_negotiate_flags_4000 = -1;
+static int hf_ntlmssp_negotiate_flags_8000 = -1;
+static int hf_ntlmssp_negotiate_flags_10000 = -1;
+static int hf_ntlmssp_negotiate_flags_20000 = -1;
+static int hf_ntlmssp_negotiate_flags_40000 = -1;
+static int hf_ntlmssp_negotiate_flags_80000 = -1;
+static int hf_ntlmssp_negotiate_flags_100000 = -1;
+static int hf_ntlmssp_negotiate_flags_200000 = -1;
+static int hf_ntlmssp_negotiate_flags_400000 = -1;
+static int hf_ntlmssp_negotiate_flags_800000 = -1;
+static int hf_ntlmssp_negotiate_flags_1000000 = -1;
+static int hf_ntlmssp_negotiate_flags_2000000 = -1;
+static int hf_ntlmssp_negotiate_flags_4000000 = -1;
+static int hf_ntlmssp_negotiate_flags_8000000 = -1;
+static int hf_ntlmssp_negotiate_flags_10000000 = -1;
+static int hf_ntlmssp_negotiate_flags_20000000 = -1;
+static int hf_ntlmssp_negotiate_flags_40000000 = -1;
+static int hf_ntlmssp_negotiate_flags_80000000 = -1;
+/* static int hf_ntlmssp_negotiate_workstation_strlen = -1; */
+/* static int hf_ntlmssp_negotiate_workstation_maxlen = -1; */
+/* static int hf_ntlmssp_negotiate_workstation_buffer = -1; */
+static int hf_ntlmssp_negotiate_workstation = -1;
+/* static int hf_ntlmssp_negotiate_domain_strlen = -1; */
+/* static int hf_ntlmssp_negotiate_domain_maxlen = -1; */
+/* static int hf_ntlmssp_negotiate_domain_buffer = -1; */
+static int hf_ntlmssp_negotiate_domain = -1;
+static int hf_ntlmssp_ntlm_server_challenge = -1;
+static int hf_ntlmssp_ntlm_client_challenge = -1;
+static int hf_ntlmssp_reserved = -1;
+static int hf_ntlmssp_challenge_target_name = -1;
+static int hf_ntlmssp_auth_username = -1;
+static int hf_ntlmssp_auth_domain = -1;
+static int hf_ntlmssp_auth_hostname = -1;
+static int hf_ntlmssp_auth_lmresponse = -1;
+static int hf_ntlmssp_auth_ntresponse = -1;
+static int hf_ntlmssp_auth_sesskey = -1;
+static int hf_ntlmssp_string_len = -1;
+static int hf_ntlmssp_string_maxlen = -1;
+static int hf_ntlmssp_string_offset = -1;
+static int hf_ntlmssp_blob_len = -1;
+static int hf_ntlmssp_blob_maxlen = -1;
+static int hf_ntlmssp_blob_offset = -1;
+static int hf_ntlmssp_version = -1;
+static int hf_ntlmssp_version_major = -1;
+static int hf_ntlmssp_version_minor = -1;
+static int hf_ntlmssp_version_build_number = -1;
+static int hf_ntlmssp_version_ntlm_current_revision = -1;
+
+static int hf_ntlmssp_challenge_target_info = -1;
+static int hf_ntlmssp_challenge_target_info_len = -1;
+static int hf_ntlmssp_challenge_target_info_maxlen = -1;
+static int hf_ntlmssp_challenge_target_info_offset = -1;
+
+static int hf_ntlmssp_challenge_target_info_item_type = -1;
+static int hf_ntlmssp_challenge_target_info_item_len = -1;
+
+static int hf_ntlmssp_challenge_target_info_end = -1;
+static int hf_ntlmssp_challenge_target_info_nb_computer_name = -1;
+static int hf_ntlmssp_challenge_target_info_nb_domain_name = -1;
+static int hf_ntlmssp_challenge_target_info_dns_computer_name = -1;
+static int hf_ntlmssp_challenge_target_info_dns_domain_name = -1;
+static int hf_ntlmssp_challenge_target_info_dns_tree_name = -1;
+static int hf_ntlmssp_challenge_target_info_flags = -1;
+static int hf_ntlmssp_challenge_target_info_timestamp = -1;
+static int hf_ntlmssp_challenge_target_info_restrictions = -1;
+static int hf_ntlmssp_challenge_target_info_target_name =-1;
+static int hf_ntlmssp_challenge_target_info_channel_bindings =-1;
+
+static int hf_ntlmssp_ntlmv2_response_item_type = -1;
+static int hf_ntlmssp_ntlmv2_response_item_len = -1;
+
+static int hf_ntlmssp_ntlmv2_response_end = -1;
+static int hf_ntlmssp_ntlmv2_response_nb_computer_name = -1;
+static int hf_ntlmssp_ntlmv2_response_nb_domain_name = -1;
+static int hf_ntlmssp_ntlmv2_response_dns_computer_name = -1;
+static int hf_ntlmssp_ntlmv2_response_dns_domain_name = -1;
+static int hf_ntlmssp_ntlmv2_response_dns_tree_name = -1;
+static int hf_ntlmssp_ntlmv2_response_flags = -1;
+static int hf_ntlmssp_ntlmv2_response_timestamp = -1;
+static int hf_ntlmssp_ntlmv2_response_restrictions = -1;
+static int hf_ntlmssp_ntlmv2_response_target_name =-1;
+static int hf_ntlmssp_ntlmv2_response_channel_bindings =-1;
+
+static int hf_ntlmssp_message_integrity_code = -1;
+static int hf_ntlmssp_verf = -1;
+static int hf_ntlmssp_verf_vers = -1;
+static int hf_ntlmssp_verf_body = -1;
+static int hf_ntlmssp_verf_randompad = -1;
+static int hf_ntlmssp_verf_hmacmd5 = -1;
+static int hf_ntlmssp_verf_crc32 = -1;
+static int hf_ntlmssp_verf_sequence = -1;
+/* static int hf_ntlmssp_decrypted_payload = -1; */
+
+static int hf_ntlmssp_ntlmv2_response = -1;
+static int hf_ntlmssp_ntlmv2_response_ntproofstr = -1;
+static int hf_ntlmssp_ntlmv2_response_rversion = -1;
+static int hf_ntlmssp_ntlmv2_response_hirversion = -1;
+static int hf_ntlmssp_ntlmv2_response_z = -1;
+static int hf_ntlmssp_ntlmv2_response_pad = -1;
+static int hf_ntlmssp_ntlmv2_response_time = -1;
+static int hf_ntlmssp_ntlmv2_response_chal = -1;
+
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_Version = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_Flags = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_LM_PRESENT = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_NT_PRESENT = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_REMOVED = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_CREDKEY_PRESENT = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_SHA_PRESENT = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_CredentialKey = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_CredentialKeyType = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_EncryptedCredsSize = -1;
+static int hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_EncryptedCreds = -1;
+
+static gint ett_ntlmssp = -1;
+static gint ett_ntlmssp_negotiate_flags = -1;
+static gint ett_ntlmssp_string = -1;
+static gint ett_ntlmssp_blob = -1;
+static gint ett_ntlmssp_version = -1;
+static gint ett_ntlmssp_challenge_target_info = -1;
+static gint ett_ntlmssp_challenge_target_info_item = -1;
+static gint ett_ntlmssp_ntlmv2_response = -1;
+static gint ett_ntlmssp_ntlmv2_response_item = -1;
+static gint ett_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL = -1;
+
+static expert_field ei_ntlmssp_v2_key_too_long = EI_INIT;
+static expert_field ei_ntlmssp_blob_len_too_long = EI_INIT;
+static expert_field ei_ntlmssp_target_info_attr = EI_INIT;
+static expert_field ei_ntlmssp_target_info_invalid = EI_INIT;
+static expert_field ei_ntlmssp_message_type = EI_INIT;
+static expert_field ei_ntlmssp_auth_nthash = EI_INIT;
+static expert_field ei_ntlmssp_sessionbasekey = EI_INIT;
+static expert_field ei_ntlmssp_sessionkey = EI_INIT;
+
+static dissector_handle_t ntlmssp_handle, ntlmssp_wrap_handle;
+
+/* Configuration variables */
+static const char *ntlmssp_option_nt_password = NULL;
+
+#define NTLMSSP_CONV_INFO_KEY 0
+/* Used in the conversation function */
+typedef struct _ntlmssp_info {
+ guint32 flags;
+ gboolean saw_challenge;
+ gcry_cipher_hd_t rc4_handle_client;
+ gcry_cipher_hd_t rc4_handle_server;
+ guint8 sign_key_client[NTLMSSP_KEY_LEN];
+ guint8 sign_key_server[NTLMSSP_KEY_LEN];
+ guint32 server_dest_port;
+ unsigned char server_challenge[8];
+ gboolean rc4_state_initialized;
+ ntlmssp_blob ntlm_response;
+ ntlmssp_blob lm_response;
+} ntlmssp_info;
+
+#define NTLMSSP_PACKET_INFO_KEY 1
+/* If this struct exists in the payload_decrypt, then we have already
+ decrypted it once */
+typedef struct _ntlmssp_packet_info {
+ guint8 *decrypted_payload;
+ guint8 payload_len;
+ guint8 verifier[NTLMSSP_KEY_LEN];
+ gboolean payload_decrypted;
+ gboolean verifier_decrypted;
+ int verifier_offset;
+ guint32 verifier_block_length;
+} ntlmssp_packet_info;
+
+static int
+dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_);
+
+#ifdef DEBUG_NTLMSSP
+static void printnbyte(const guint8* tab, int nb, const char* txt, const char* txt2)
+{
+ int i;
+ fprintf(stderr, "%s ", txt);
+ for (i=0; i<nb; i++)
+ {
+ fprintf(stderr, "%02X ", *(tab+i));
+ }
+ fprintf(stderr, "%s", txt2);
+}
+#if 0
+static void printnchar(const guint8* tab, int nb, char* txt, char* txt2)
+{
+ int i;
+ fprintf(stderr, "%s ", txt);
+ for (i=0; i<nb; i++)
+ {
+ fprintf(stderr, "%c", *(tab+i));
+ }
+ fprintf(stderr, "%s", txt2);
+}
+#endif
+#else
+static void printnbyte(const guint8* tab _U_, int nb _U_, const char* txt _U_, const char* txt2 _U_)
+{
+}
+#endif
+
+/*
+ * GSlist of decrypted payloads.
+ */
+static GSList *decrypted_payloads;
+
+#if 0
+static int
+LEBE_Convert(int value)
+{
+ char a, b, c, d;
+ /* Get each byte */
+ a = value&0x000000FF;
+ b = (value&0x0000FF00) >> 8;
+ c = (value&0x00FF0000) >> 16;
+ d = (value&0xFF000000) >> 24;
+ return (a << 24) | (b << 16) | (c << 8) | d;
+}
+#endif
+
+static bool
+ntlmssp_sessions_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, void *user_data _U_)
+{
+ ntlmssp_info * conv_ntlmssp_info = (ntlmssp_info *) user_data;
+ gcry_cipher_close(conv_ntlmssp_info->rc4_handle_client);
+ gcry_cipher_close(conv_ntlmssp_info->rc4_handle_server);
+ /* unregister this callback */
+ return FALSE;
+}
+
+/*
+ Perform a DES encryption with a 16-byte key and 8-byte data item.
+ It's in fact 3 susbsequent call to crypt_des_ecb with a 7-byte key.
+ Missing bytes for the key are replaced by 0;
+ Returns output in response, which is expected to be 24 bytes.
+*/
+static int
+crypt_des_ecb_long(guint8 *response,
+ const guint8 *key,
+ const guint8 *data)
+{
+ guint8 pw21[21] = { 0 }; /* 21 bytes place for the needed key */
+
+ memcpy(pw21, key, 16);
+
+ memset(response, 0, 24);
+ crypt_des_ecb(response, data, pw21);
+ crypt_des_ecb(response + 8, data, pw21 + 7);
+ crypt_des_ecb(response + 16, data, pw21 + 14);
+
+ return 1;
+}
+
+/*
+ Generate a challenge response, given an eight byte challenge and
+ either the NT or the Lan Manager password hash (16 bytes).
+ Returns output in response, which is expected to be 24 bytes.
+*/
+static int
+ntlmssp_generate_challenge_response(guint8 *response,
+ const guint8 *passhash,
+ const guint8 *challenge)
+{
+ guint8 pw21[21]; /* Password hash padded to 21 bytes */
+
+ memset(pw21, 0x0, sizeof(pw21));
+ memcpy(pw21, passhash, 16);
+
+ memset(response, 0, 24);
+
+ crypt_des_ecb(response, challenge, pw21);
+ crypt_des_ecb(response + 8, challenge, pw21 + 7);
+ crypt_des_ecb(response + 16, challenge, pw21 + 14);
+
+ return 1;
+}
+
+
+/* Ultra simple ANSI to unicode converter, will only work for ascii password...*/
+static void
+ansi_to_unicode(const char* ansi, char* unicode)
+{
+ size_t input_len;
+ size_t i;
+
+ input_len = strlen(ansi);
+ if (unicode != NULL) {
+ for (i = 0; i < (input_len); i++) {
+ unicode[i * 2] = ansi[i];
+ unicode[i * 2 + 1] = 0;
+ }
+ unicode[2 * input_len] = '\0';
+ }
+}
+
+/* This function generate the Key Exchange Key (KXKEY)
+ * Depending on the flags this key will either be used to encrypt the exported session key
+ * or will be used directly as exported session key.
+ * Exported session key is the key that will be used for sealing and signing communication
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/d86303b5-b29e-4fb9-b119-77579c761370
+ */
+
+static void
+get_keyexchange_key(unsigned char keyexchangekey[NTLMSSP_KEY_LEN], const unsigned char sessionbasekey[NTLMSSP_KEY_LEN], const unsigned char lm_challenge_response[24], int flags)
+{
+ guint8 basekey[NTLMSSP_KEY_LEN];
+ guint8 zeros[24] = { 0 };
+
+ memset(keyexchangekey, 0, NTLMSSP_KEY_LEN);
+ memset(basekey, 0, NTLMSSP_KEY_LEN);
+ /* sessionbasekey is either derived from lm_hash or from nt_hash depending on the key type negotiated */
+ memcpy(basekey, sessionbasekey, 8);
+ memset(basekey, 0xBD, 8);
+ if (flags&NTLMSSP_NEGOTIATE_LM_KEY) {
+ /*data, key*/
+ crypt_des_ecb(keyexchangekey, lm_challenge_response, basekey);
+ crypt_des_ecb(keyexchangekey+8, lm_challenge_response, basekey+7);
+ }
+ else {
+ if (flags&NTLMSSP_REQUEST_NON_NT_SESSION_KEY) {
+ /*People from samba tends to use the same function in this case than in the previous one but with 0 data
+ * it's not clear that it produce the good result
+ * memcpy(keyexchangekey, lm_hash, 8);
+ * Let's trust samba implementation it mights seem weird but they are more often right than the spec!
+ */
+ crypt_des_ecb(keyexchangekey, zeros, basekey);
+ crypt_des_ecb(keyexchangekey+8, zeros, basekey+7);
+ }
+ else {
+ /* it is stated page 65 of NTLM SSP spec: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/d86303b5-b29e-4fb9-b119-77579c761370
+ * that sessionbasekey should be encrypted with hmac_md5 using the concat of both challenge when it's NTLM v1 + extended session security but it turns out to be wrong!
+ */
+ memcpy(keyexchangekey, sessionbasekey, NTLMSSP_KEY_LEN);
+ }
+ }
+}
+
+guint32
+get_md4pass_list(wmem_allocator_t *pool, md4_pass** p_pass_list)
+{
+#if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)
+ guint32 nb_pass = 0;
+ enc_key_t *ek;
+ const char* password = ntlmssp_option_nt_password;
+ unsigned char nt_hash[NTLMSSP_KEY_LEN];
+ char password_unicode[256];
+ md4_pass* pass_list;
+ int i;
+
+ *p_pass_list = NULL;
+ read_keytab_file_from_preferences();
+
+ for (ek=enc_key_list; ek; ek=ek->next) {
+ if (NTLMSSP_EK_IS_NT4HASH(ek)) {
+ nb_pass++;
+ }
+ }
+ memset(password_unicode, 0, sizeof(password_unicode));
+ memset(nt_hash, 0, NTLMSSP_KEY_LEN);
+ /* Compute the NT hash of the provided password, even if empty */
+ if (strlen(password) < 129) {
+ int password_len;
+ nb_pass++;
+ password_len = (int)strlen(password);
+ ansi_to_unicode(password, password_unicode);
+ gcry_md_hash_buffer(GCRY_MD_MD4, nt_hash, password_unicode, password_len*2);
+ }
+ if (nb_pass == 0) {
+ /* Unable to calculate the session key without a valid password (128 chars or less) ......*/
+ return 0;
+ }
+ i = 0;
+ *p_pass_list = (md4_pass *)wmem_alloc0(pool, nb_pass*sizeof(md4_pass));
+ pass_list = *p_pass_list;
+
+ if (memcmp(nt_hash, gbl_zeros, NTLMSSP_KEY_LEN) != 0) {
+ memcpy(pass_list[i].md4, nt_hash, NTLMSSP_KEY_LEN);
+ snprintf(pass_list[i].key_origin, NTLMSSP_MAX_ORIG_LEN,
+ "<Global NT Password>");
+ i = 1;
+ }
+ for (ek=enc_key_list; ek; ek=ek->next) {
+ if (NTLMSSP_EK_IS_NT4HASH(ek)) {
+ memcpy(pass_list[i].md4, ek->keyvalue, NTLMSSP_KEY_LEN);
+ memcpy(pass_list[i].key_origin, ek->key_origin,
+ MIN(sizeof(pass_list[i].key_origin),sizeof(ek->key_origin)));
+ i++;
+ }
+ }
+ return nb_pass;
+#else /* !(defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)) */
+ (void) pool;
+ *p_pass_list = NULL;
+ return 0;
+#endif /* !(defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)) */
+}
+
+/* Create an NTLMSSP version 2 key
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/5e550938-91d4-459f-b67d-75d70009e3f3
+ */
+static void
+create_ntlmssp_v2_key(const guint8 *serverchallenge, const guint8 *clientchallenge,
+ guint8 *sessionkey , const guint8 *encryptedsessionkey , int flags ,
+ const ntlmssp_blob *ntlm_response, const ntlmssp_blob *lm_response _U_, ntlmssp_header_t *ntlmssph,
+ packet_info *pinfo, proto_tree *ntlmssp_tree)
+{
+/* static const would be nicer, but -Werror=vla does not like it */
+#define DOMAIN_NAME_BUF_SIZE 512
+#define USER_BUF_SIZE 256
+#define BUF_SIZE (DOMAIN_NAME_BUF_SIZE + USER_BUF_SIZE)
+ char domain_name_unicode[DOMAIN_NAME_BUF_SIZE];
+ char user_uppercase[USER_BUF_SIZE];
+ char buf[BUF_SIZE];
+ /*guint8 md4[NTLMSSP_KEY_LEN];*/
+ unsigned char nt_hash[NTLMSSP_KEY_LEN];
+ unsigned char nt_proof[NTLMSSP_KEY_LEN];
+ unsigned char ntowfv2[NTLMSSP_KEY_LEN];
+ guint8 sessionbasekey[NTLMSSP_KEY_LEN];
+ guint8 keyexchangekey[NTLMSSP_KEY_LEN];
+ guint8 lm_challenge_response[24];
+ guint32 i;
+ guint32 j;
+ gcry_cipher_hd_t rc4_handle;
+ size_t user_len;
+ size_t domain_len;
+ md4_pass *pass_list = NULL;
+ const md4_pass *used_md4 = NULL;
+ guint32 nb_pass = 0;
+ gboolean found = FALSE;
+
+ /* We are going to try password encrypted in keytab as well, it's an idea of Stefan Metzmacher <metze@samba.org>
+ * The idea is to be able to test all the key of domain in once and to be able to decode the NTLM dialogs */
+
+ memset(sessionkey, 0, NTLMSSP_KEY_LEN);
+ nb_pass = get_md4pass_list(pinfo->pool, &pass_list);
+ i = 0;
+ memset(user_uppercase, 0, USER_BUF_SIZE);
+ user_len = strlen(ntlmssph->acct_name);
+ if (user_len < USER_BUF_SIZE / 2) {
+ memset(buf, 0, BUF_SIZE);
+ ansi_to_unicode(ntlmssph->acct_name, buf);
+ for (j = 0; j < (2*user_len); j++) {
+ if (buf[j] != '\0') {
+ user_uppercase[j] = g_ascii_toupper(buf[j]);
+ }
+ }
+ }
+ else {
+ /* Unable to calculate the session not enough space in buffer, note this is unlikely to happen but ......*/
+ return;
+ }
+ domain_len = strlen(ntlmssph->domain_name);
+ if (domain_len < DOMAIN_NAME_BUF_SIZE / 2) {
+ ansi_to_unicode(ntlmssph->domain_name, domain_name_unicode);
+ }
+ else {
+ /* Unable to calculate the session not enough space in buffer, note this is unlikely to happen but ......*/
+ return;
+ }
+ while (i < nb_pass) {
+ #ifdef DEBUG_NTLMSSP
+ fprintf(stderr, "Turn %d, ", i);
+ #endif
+ used_md4 = &pass_list[i];
+ memcpy(nt_hash, pass_list[i].md4, NTLMSSP_KEY_LEN);
+ printnbyte(nt_hash, NTLMSSP_KEY_LEN, "Current NT hash: ", "\n");
+ i++;
+ /* NTOWFv2 computation */
+ memset(buf, 0, BUF_SIZE);
+ memcpy(buf, user_uppercase, user_len*2);
+ memcpy(buf+user_len*2, domain_name_unicode, domain_len*2);
+ if (ws_hmac_buffer(GCRY_MD_MD5, ntowfv2, buf, domain_len*2+user_len*2, nt_hash, NTLMSSP_KEY_LEN)) {
+ return;
+ }
+ printnbyte(ntowfv2, NTLMSSP_KEY_LEN, "NTOWFv2: ", "\n");
+
+ /* LM response */
+ memset(buf, 0, BUF_SIZE);
+ memcpy(buf, serverchallenge, 8);
+ memcpy(buf+8, clientchallenge, 8);
+ if (ws_hmac_buffer(GCRY_MD_MD5, lm_challenge_response, buf, NTLMSSP_KEY_LEN, ntowfv2, NTLMSSP_KEY_LEN)) {
+ return;
+ }
+ memcpy(lm_challenge_response+NTLMSSP_KEY_LEN, clientchallenge, 8);
+ printnbyte(lm_challenge_response, 24, "LM Response: ", "\n");
+
+ /* NT proof = First NTLMSSP_KEY_LEN bytes of NT response */
+ memset(buf, 0, BUF_SIZE);
+ memcpy(buf, serverchallenge, 8);
+ memcpy(buf+8, ntlm_response->contents+NTLMSSP_KEY_LEN, MIN(BUF_SIZE - 8, ntlm_response->length-NTLMSSP_KEY_LEN));
+ if (ws_hmac_buffer(GCRY_MD_MD5, nt_proof, buf, ntlm_response->length-8, ntowfv2, NTLMSSP_KEY_LEN)) {
+ return;
+ }
+ printnbyte(nt_proof, NTLMSSP_KEY_LEN, "NT proof: ", "\n");
+ if (!memcmp(nt_proof, ntlm_response->contents, NTLMSSP_KEY_LEN)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+
+ if (ws_hmac_buffer(GCRY_MD_MD5, sessionbasekey, nt_proof, NTLMSSP_KEY_LEN, ntowfv2, NTLMSSP_KEY_LEN)) {
+ return;
+ }
+
+ get_keyexchange_key(keyexchangekey, sessionbasekey, lm_challenge_response, flags);
+ /* now decrypt session key if needed and setup sessionkey for decrypting further communications */
+ if (flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ {
+ memcpy(sessionkey, encryptedsessionkey, NTLMSSP_KEY_LEN);
+ if (!gcry_cipher_open(&rc4_handle, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ if (!gcry_cipher_setkey(rc4_handle, keyexchangekey, NTLMSSP_KEY_LEN)) {
+ gcry_cipher_decrypt(rc4_handle, sessionkey, NTLMSSP_KEY_LEN, NULL, 0);
+ }
+ gcry_cipher_close(rc4_handle);
+ }
+ }
+ else
+ {
+ memcpy(sessionkey, keyexchangekey, NTLMSSP_KEY_LEN);
+ }
+
+ memcpy(ntlmssph->session_key, sessionkey, NTLMSSP_KEY_LEN);
+
+ if (used_md4 == NULL) {
+ return;
+ }
+ expert_add_info_format(pinfo, proto_tree_get_parent(ntlmssp_tree),
+ &ei_ntlmssp_auth_nthash,
+ "NTLMv2 authenticated using %s (%02x%02x%02x%02x...)",
+ used_md4->key_origin,
+ used_md4->md4[0] & 0xFF, used_md4->md4[1] & 0xFF,
+ used_md4->md4[2] & 0xFF, used_md4->md4[3] & 0xFF);
+ expert_add_info_format(pinfo, proto_tree_get_parent(ntlmssp_tree),
+ &ei_ntlmssp_sessionbasekey,
+ "NTLMv2 BaseSessionKey ("
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ ")",
+ sessionbasekey[0] & 0xFF, sessionbasekey[1] & 0xFF,
+ sessionbasekey[2] & 0xFF, sessionbasekey[3] & 0xFF,
+ sessionbasekey[4] & 0xFF, sessionbasekey[5] & 0xFF,
+ sessionbasekey[6] & 0xFF, sessionbasekey[7] & 0xFF,
+ sessionbasekey[8] & 0xFF, sessionbasekey[9] & 0xFF,
+ sessionbasekey[10] & 0xFF, sessionbasekey[11] & 0xFF,
+ sessionbasekey[12] & 0xFF, sessionbasekey[13] & 0xFF,
+ sessionbasekey[14] & 0xFF, sessionbasekey[15] & 0xFF);
+ if (memcmp(sessionbasekey, sessionkey, NTLMSSP_KEY_LEN) == 0) {
+ return;
+ }
+ expert_add_info_format(pinfo, proto_tree_get_parent(ntlmssp_tree),
+ &ei_ntlmssp_sessionkey,
+ "NTLMSSP SessionKey ("
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ ")",
+ sessionkey[0] & 0xFF, sessionkey[1] & 0xFF,
+ sessionkey[2] & 0xFF, sessionkey[3] & 0xFF,
+ sessionkey[4] & 0xFF, sessionkey[5] & 0xFF,
+ sessionkey[6] & 0xFF, sessionkey[7] & 0xFF,
+ sessionkey[8] & 0xFF, sessionkey[9] & 0xFF,
+ sessionkey[10] & 0xFF, sessionkey[11] & 0xFF,
+ sessionkey[12] & 0xFF, sessionkey[13] & 0xFF,
+ sessionkey[14] & 0xFF, sessionkey[15] & 0xFF);
+}
+
+ /* Create an NTLMSSP version 1 key
+ * That is more complicated logic and methods and user challenge as well.
+ * password points to the ANSI password to encrypt, challenge points to
+ * the 8 octet challenge string
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/464551a8-9fc4-428e-b3d3-bc5bfb2e73a5
+ */
+static void
+create_ntlmssp_v1_key(const guint8 *serverchallenge, const guint8 *clientchallenge,
+ guint8 *sessionkey, const guint8 *encryptedsessionkey, int flags,
+ const guint8 *ref_nt_challenge_response, const guint8 *ref_lm_challenge_response,
+ ntlmssp_header_t *ntlmssph,
+ packet_info *pinfo, proto_tree *ntlmssp_tree)
+{
+ const char *password = ntlmssp_option_nt_password;
+ unsigned char lm_password_upper[NTLMSSP_KEY_LEN];
+ unsigned char lm_hash[NTLMSSP_KEY_LEN];
+ unsigned char nt_hash[NTLMSSP_KEY_LEN];
+ unsigned char challenges_hash_first8[8];
+ unsigned char challenges[NTLMSSP_KEY_LEN];
+ guint8 md4[NTLMSSP_KEY_LEN];
+ guint8 nb_pass = 0;
+ guint8 sessionbasekey[NTLMSSP_KEY_LEN];
+ guint8 keyexchangekey[NTLMSSP_KEY_LEN];
+ guint8 lm_challenge_response[24];
+ guint8 nt_challenge_response[24];
+ gcry_cipher_hd_t rc4_handle;
+ gcry_md_hd_t md5_handle;
+ char password_unicode[256];
+ size_t password_len;
+ unsigned int i;
+ gboolean found = FALSE;
+ md4_pass *pass_list = NULL;
+ const md4_pass *used_md4 = NULL;
+
+ // "A Boolean setting that SHOULD<35> control using the NTLM response for the LM response to the server challenge when NTLMv1 authentication is used. The default value of this state variable is TRUE."
+ // "<35> Section 3.1.1.1: Windows NT Server 4.0 SP3 does not support providing NTLM instead of LM responses."
+ // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/f711d059-3983-4b9d-afbb-ff2f8c97ffbf
+ static const bool NoLMResponseNTLMv1 = TRUE;
+
+ static const unsigned char lmhash_key[] =
+ {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; // "KGS!@#$%"
+
+ /* Create a NT hash of the input password, even if empty */
+ // NTOWFv1 as defined in https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/464551a8-9fc4-428e-b3d3-bc5bfb2e73a5
+ password_len = strlen(password);
+ /*Do not forget to free password*/
+ ansi_to_unicode(password, password_unicode);
+ gcry_md_hash_buffer(GCRY_MD_MD4, nt_hash, password_unicode, password_len*2);
+
+ if ((flags & NTLMSSP_NEGOTIATE_LM_KEY && !(flags & NoLMResponseNTLMv1)) || !(flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) || !(flags & NTLMSSP_NEGOTIATE_NTLM)) {
+ /* Create a LM hash of the input password, even if empty */
+ // LMOWFv1 as defined in https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/464551a8-9fc4-428e-b3d3-bc5bfb2e73a5
+ /* Truncate password if too long */
+ if (password_len > NTLMSSP_KEY_LEN)
+ password_len = NTLMSSP_KEY_LEN;
+
+ memset(lm_password_upper, 0, sizeof(lm_password_upper));
+ for (i = 0; i < password_len; i++) {
+ lm_password_upper[i] = g_ascii_toupper(password[i]);
+ }
+
+ crypt_des_ecb(lm_hash, lmhash_key, lm_password_upper);
+ crypt_des_ecb(lm_hash+8, lmhash_key, lm_password_upper+7);
+ ntlmssp_generate_challenge_response(lm_challenge_response,
+ lm_hash, serverchallenge);
+ memcpy(sessionbasekey, lm_hash, NTLMSSP_KEY_LEN);
+ }
+ else {
+
+ memset(lm_challenge_response, 0, 24);
+ if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+ nb_pass = get_md4pass_list(pinfo->pool, &pass_list);
+ i = 0;
+ while (i < nb_pass) {
+ /*fprintf(stderr, "Turn %d, ", i);*/
+ used_md4 = &pass_list[i];
+ memcpy(nt_hash, pass_list[i].md4, NTLMSSP_KEY_LEN);
+ /*printnbyte(nt_hash, NTLMSSP_KEY_LEN, "Current NT hash: ", "\n");*/
+ i++;
+ if(clientchallenge){
+ memcpy(lm_challenge_response, clientchallenge, 8);
+ }
+ if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) {
+ break;
+ }
+ gcry_md_write(md5_handle, serverchallenge, 8);
+ gcry_md_write(md5_handle, clientchallenge, 8);
+ memcpy(challenges_hash_first8, gcry_md_read(md5_handle, 0), 8);
+ gcry_md_close(md5_handle);
+ crypt_des_ecb_long(nt_challenge_response, nt_hash, challenges_hash_first8);
+ if (ref_nt_challenge_response && !memcmp(ref_nt_challenge_response, nt_challenge_response, 24)) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ else {
+ crypt_des_ecb_long(nt_challenge_response, nt_hash, serverchallenge);
+ if (NoLMResponseNTLMv1) {
+ memcpy(lm_challenge_response, nt_challenge_response, 24);
+ }
+ else {
+ crypt_des_ecb_long(lm_challenge_response, lm_hash, serverchallenge);
+ }
+ if (ref_nt_challenge_response &&
+ !memcmp(ref_nt_challenge_response, nt_challenge_response, 24) &&
+ ref_lm_challenge_response &&
+ !memcmp(ref_lm_challenge_response, lm_challenge_response, 24))
+ {
+ found = TRUE;
+ }
+ }
+ /* So it's clearly not like this that's put into NTLMSSP doc but after some digging into samba code I'm quite confident
+ * that sessionbasekey should be based md4(nt_hash) only in the case of some NT auth
+ * Otherwise it should be lm_hash ...*/
+ gcry_md_hash_buffer(GCRY_MD_MD4, md4, nt_hash, NTLMSSP_KEY_LEN);
+ if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+ memcpy(challenges, serverchallenge, 8);
+ if(clientchallenge){
+ memcpy(challenges+8, clientchallenge, 8);
+ }
+ if (ws_hmac_buffer(GCRY_MD_MD5, sessionbasekey, challenges, NTLMSSP_KEY_LEN, md4, NTLMSSP_KEY_LEN)) {
+ return;
+ }
+ }
+ else {
+ memcpy(sessionbasekey, md4, NTLMSSP_KEY_LEN);
+ }
+ }
+
+ if (!found) {
+ return;
+ }
+
+ get_keyexchange_key(keyexchangekey, sessionbasekey, lm_challenge_response, flags);
+ memset(sessionkey, 0, NTLMSSP_KEY_LEN);
+ /*printnbyte(nt_challenge_response, 24, "NT challenge response", "\n");
+ printnbyte(lm_challenge_response, 24, "LM challenge response", "\n");*/
+ /* now decrypt session key if needed and setup sessionkey for decrypting further communications */
+ if (flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ {
+ if(encryptedsessionkey){
+ memcpy(sessionkey, encryptedsessionkey, NTLMSSP_KEY_LEN);
+ }
+ if (!gcry_cipher_open(&rc4_handle, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ if (!gcry_cipher_setkey(rc4_handle, keyexchangekey, NTLMSSP_KEY_LEN)) {
+ gcry_cipher_decrypt(rc4_handle, sessionkey, NTLMSSP_KEY_LEN, NULL, 0);
+ }
+ gcry_cipher_close(rc4_handle);
+ }
+ }
+ else
+ {
+ memcpy(sessionkey, keyexchangekey, NTLMSSP_KEY_LEN);
+ }
+ memcpy(ntlmssph->session_key, sessionkey, NTLMSSP_KEY_LEN);
+
+ if (used_md4 == NULL) {
+ return;
+ }
+ expert_add_info_format(pinfo, proto_tree_get_parent(ntlmssp_tree),
+ &ei_ntlmssp_auth_nthash,
+ "NTLMv1 authenticated using %s (%02x%02x%02x%02x...)",
+ used_md4->key_origin,
+ used_md4->md4[0] & 0xFF, used_md4->md4[1] & 0xFF,
+ used_md4->md4[2] & 0xFF, used_md4->md4[3] & 0xFF);
+ expert_add_info_format(pinfo, proto_tree_get_parent(ntlmssp_tree),
+ &ei_ntlmssp_sessionbasekey,
+ "NTLMv1 BaseSessionKey ("
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ ")",
+ sessionbasekey[0] & 0xFF, sessionbasekey[1] & 0xFF,
+ sessionbasekey[2] & 0xFF, sessionbasekey[3] & 0xFF,
+ sessionbasekey[4] & 0xFF, sessionbasekey[5] & 0xFF,
+ sessionbasekey[6] & 0xFF, sessionbasekey[7] & 0xFF,
+ sessionbasekey[8] & 0xFF, sessionbasekey[9] & 0xFF,
+ sessionbasekey[10] & 0xFF, sessionbasekey[11] & 0xFF,
+ sessionbasekey[12] & 0xFF, sessionbasekey[13] & 0xFF,
+ sessionbasekey[14] & 0xFF, sessionbasekey[15] & 0xFF);
+ if (memcmp(sessionbasekey, sessionkey, NTLMSSP_KEY_LEN) == 0) {
+ return;
+ }
+ expert_add_info_format(pinfo, proto_tree_get_parent(ntlmssp_tree),
+ &ei_ntlmssp_sessionkey,
+ "NTLMSSP SessionKey ("
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ "%02x%02x%02x%02x"
+ ")",
+ sessionkey[0] & 0xFF, sessionkey[1] & 0xFF,
+ sessionkey[2] & 0xFF, sessionkey[3] & 0xFF,
+ sessionkey[4] & 0xFF, sessionkey[5] & 0xFF,
+ sessionkey[6] & 0xFF, sessionkey[7] & 0xFF,
+ sessionkey[8] & 0xFF, sessionkey[9] & 0xFF,
+ sessionkey[10] & 0xFF, sessionkey[11] & 0xFF,
+ sessionkey[12] & 0xFF, sessionkey[13] & 0xFF,
+ sessionkey[14] & 0xFF, sessionkey[15] & 0xFF);
+}
+
+void
+ntlmssp_create_session_key(packet_info *pinfo,
+ proto_tree *tree,
+ ntlmssp_header_t *ntlmssph,
+ int flags,
+ const guint8 *server_challenge,
+ const guint8 *encryptedsessionkey,
+ const ntlmssp_blob *ntlm_response,
+ const ntlmssp_blob *lm_response)
+{
+ guint8 client_challenge[8] = {0, };
+ guint8 sessionkey[NTLMSSP_KEY_LEN] = {0, };
+
+ if (ntlm_response->length > 24)
+ {
+ /*
+ * [MS-NLMP] 2.2.2.8 NTLM2 V2 Response: NTLMv2_RESPONSE has
+ * the 2.2.2.7 "NTLM v2: NTLMv2_CLIENT_CHALLENGE" at offset 16.
+ * Within that ChallengeFromClient is at offset 16, that means
+ * it's at offset 32 in total.
+ *
+ * Note that value is only used for the LM_response of NTLMv2.
+ */
+ if (ntlm_response->length >= 40) {
+ memcpy(client_challenge,
+ ntlm_response->contents+32, 8);
+ }
+ create_ntlmssp_v2_key(server_challenge,
+ client_challenge,
+ sessionkey,
+ encryptedsessionkey,
+ flags,
+ ntlm_response,
+ lm_response,
+ ntlmssph,
+ pinfo,
+ tree);
+ }
+ else if (ntlm_response->length == 24 && lm_response->length == 24)
+ {
+ memcpy(client_challenge, lm_response->contents, 8);
+
+ create_ntlmssp_v1_key(server_challenge,
+ client_challenge,
+ sessionkey,
+ encryptedsessionkey,
+ flags,
+ ntlm_response->contents,
+ lm_response->contents,
+ ntlmssph,
+ pinfo,
+ tree);
+ }
+}
+
+static void
+get_signing_key(guint8 *sign_key_server, guint8* sign_key_client, const guint8 key[NTLMSSP_KEY_LEN], int keylen)
+{
+ gcry_md_hd_t md5_handle;
+
+ memset(sign_key_client, 0, NTLMSSP_KEY_LEN);
+ memset(sign_key_server, 0, NTLMSSP_KEY_LEN);
+ if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) {
+ return;
+ }
+ gcry_md_write(md5_handle, key, keylen);
+ gcry_md_write(md5_handle, CLIENT_SIGN_TEXT, strlen(CLIENT_SIGN_TEXT)+1); // +1 to get the final null-byte
+ memcpy(sign_key_client, gcry_md_read(md5_handle, 0), NTLMSSP_KEY_LEN);
+ gcry_md_reset(md5_handle);
+ gcry_md_write(md5_handle, key, keylen);
+ gcry_md_write(md5_handle, SERVER_SIGN_TEXT, strlen(SERVER_SIGN_TEXT)+1); // +1 to get the final null-byte
+ memcpy(sign_key_server, gcry_md_read(md5_handle, 0), NTLMSSP_KEY_LEN);
+ gcry_md_close(md5_handle);
+}
+
+/* We return either a 128 or 64 bit key
+ */
+static void
+get_sealing_rc4key(const guint8 exportedsessionkey[NTLMSSP_KEY_LEN] , const int flags , int *keylen ,
+ guint8 *clientsealkey , guint8 *serversealkey)
+{
+ gcry_md_hd_t md5_handle;
+
+ memset(clientsealkey, 0, NTLMSSP_KEY_LEN);
+ memset(serversealkey, 0, NTLMSSP_KEY_LEN);
+ memcpy(clientsealkey, exportedsessionkey, NTLMSSP_KEY_LEN);
+ if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)
+ {
+ if (flags & NTLMSSP_NEGOTIATE_128)
+ {
+ /* The exportedsessionkey has already the good length just update the length*/
+ *keylen = 16;
+ }
+ else
+ {
+ if (flags & NTLMSSP_NEGOTIATE_56)
+ {
+ memset(clientsealkey+7, 0, 9);
+ *keylen = 7;
+ }
+ else
+ {
+ memset(clientsealkey+5, 0, 11);
+ *keylen = 5;
+ }
+ }
+ memcpy(serversealkey, clientsealkey, NTLMSSP_KEY_LEN);
+ if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) {
+ return;
+ }
+ gcry_md_write(md5_handle, clientsealkey, *keylen);
+ gcry_md_write(md5_handle, CLIENT_SEAL_TEXT, strlen(CLIENT_SEAL_TEXT)+1); // +1 to get the final null-byte
+ memcpy(clientsealkey, gcry_md_read(md5_handle, 0), NTLMSSP_KEY_LEN);
+ gcry_md_reset(md5_handle);
+ gcry_md_write(md5_handle, serversealkey, *keylen);
+ gcry_md_write(md5_handle, SERVER_SEAL_TEXT, strlen(SERVER_SEAL_TEXT)+1); // +1 to get the final null-byte
+ memcpy(serversealkey, gcry_md_read(md5_handle, 0), NTLMSSP_KEY_LEN);
+ gcry_md_close(md5_handle);
+ }
+ else
+ {
+ if (flags & NTLMSSP_NEGOTIATE_128)
+ {
+ /* The exportedsessionkey has already the good length just update the length*/
+ *keylen = 16;
+ }
+ else
+ {
+ *keylen = 8;
+ if (flags & NTLMSSP_NEGOTIATE_56)
+ {
+ memset(clientsealkey+7, 0, 9);
+ }
+ else
+ {
+ memset(clientsealkey+5, 0, 11);
+ clientsealkey[5]=0xe5;
+ clientsealkey[6]=0x38;
+ clientsealkey[7]=0xb0;
+ }
+ }
+ memcpy(serversealkey, clientsealkey,*keylen);
+ }
+}
+/* Create an NTLMSSP version 1 key.
+ * password points to the ANSI password to encrypt, challenge points to
+ * the 8 octet challenge string, key128 will do a 128 bit key if set to 1,
+ * otherwise it will do a 40 bit key. The result is stored in
+ * sspkey (expected to be NTLMSSP_KEY_LEN octets)
+ */
+/* dissect a string - header area contains:
+ two byte len
+ two byte maxlen
+ four byte offset of string in data area
+ The function returns the offset at the end of the string header,
+ but the 'end' parameter returns the offset of the end of the string itself
+ The 'start' parameter returns the offset of the beginning of the string
+ If there's no string, just use the offset of the end of the tvb as start/end.
+*/
+static int
+dissect_ntlmssp_string (tvbuff_t *tvb, int offset,
+ proto_tree *ntlmssp_tree,
+ gboolean unicode_strings,
+ int string_hf, int *start, int *end,
+ const guint8 **stringp)
+{
+ proto_tree *tree = NULL;
+ proto_item *tf = NULL;
+ gint16 string_length = tvb_get_letohs(tvb, offset);
+ gint16 string_maxlen = tvb_get_letohs(tvb, offset+2);
+ gint32 string_offset = tvb_get_letohl(tvb, offset+4);
+
+ *start = (string_offset > offset+8 ? string_offset : (signed)tvb_reported_length(tvb));
+ if (0 == string_length) {
+ *end = *start;
+ if (ntlmssp_tree)
+ proto_tree_add_string(ntlmssp_tree, string_hf, tvb,
+ offset, 8, "NULL");
+ if (stringp != NULL)
+ *stringp = "";
+ return offset+8;
+ }
+
+ if (unicode_strings) {
+ /* UTF-16 string; must be 2-byte aligned */
+ if ((string_offset & 1) != 0)
+ string_offset++;
+ }
+ tf = proto_tree_add_item_ret_string(ntlmssp_tree, string_hf, tvb,
+ string_offset, string_length,
+ unicode_strings ? ENC_UTF_16|ENC_LITTLE_ENDIAN : ENC_ASCII|ENC_NA,
+ wmem_packet_scope(), stringp);
+ tree = proto_item_add_subtree(tf, ett_ntlmssp_string);
+ proto_tree_add_uint(tree, hf_ntlmssp_string_len,
+ tvb, offset, 2, string_length);
+ offset += 2;
+ proto_tree_add_uint(tree, hf_ntlmssp_string_maxlen,
+ tvb, offset, 2, string_maxlen);
+ offset += 2;
+ proto_tree_add_uint(tree, hf_ntlmssp_string_offset,
+ tvb, offset, 4, string_offset);
+ offset += 4;
+
+ *end = string_offset + string_length;
+ return offset;
+}
+
+/* dissect a generic blob - header area contains:
+ two byte len
+ two byte maxlen
+ four byte offset of blob in data area
+ The function returns the offset at the end of the blob header,
+ but the 'end' parameter returns the offset of the end of the blob itself
+*/
+static int
+dissect_ntlmssp_blob (tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *ntlmssp_tree, int offset,
+ int blob_hf, int *end, ntlmssp_blob *result)
+{
+ proto_item *tf = NULL;
+ proto_tree *tree = NULL;
+ guint16 blob_length = tvb_get_letohs(tvb, offset);
+ guint16 blob_maxlen = tvb_get_letohs(tvb, offset+2);
+ guint32 blob_offset = tvb_get_letohl(tvb, offset+4);
+
+ if (0 == blob_length) {
+ *end = (blob_offset > ((guint)offset)+8 ? blob_offset : ((guint)offset)+8);
+ proto_tree_add_bytes_format_value(ntlmssp_tree, blob_hf, tvb, offset, 8, NULL, "Empty");
+ result->length = 0;
+ result->contents = NULL;
+ return offset+8;
+ }
+
+ if (ntlmssp_tree) {
+ tf = proto_tree_add_item (ntlmssp_tree, blob_hf, tvb,
+ blob_offset, blob_length, ENC_NA);
+ tree = proto_item_add_subtree(tf, ett_ntlmssp_blob);
+ }
+ proto_tree_add_uint(tree, hf_ntlmssp_blob_len,
+ tvb, offset, 2, blob_length);
+ offset += 2;
+ proto_tree_add_uint(tree, hf_ntlmssp_blob_maxlen,
+ tvb, offset, 2, blob_maxlen);
+ offset += 2;
+ proto_tree_add_uint(tree, hf_ntlmssp_blob_offset,
+ tvb, offset, 4, blob_offset);
+ offset += 4;
+
+ *end = blob_offset + blob_length;
+
+ if (blob_length < NTLMSSP_BLOB_MAX_SIZE) {
+ result->length = blob_length;
+ result->contents = (guint8 *)tvb_memdup(wmem_file_scope(), tvb, blob_offset, blob_length);
+ } else {
+ expert_add_info_format(pinfo, tf, &ei_ntlmssp_v2_key_too_long,
+ "NTLM v2 key is %d bytes long, too big for our %d buffer", blob_length, NTLMSSP_BLOB_MAX_SIZE);
+ result->length = 0;
+ result->contents = NULL;
+ }
+
+ /*
+ * XXX - for LmChallengeResponse (hf_ntlmssp_auth_lmresponse), should
+ * we have a field for both Response (2.2.2.3 "LM_RESPONSE" and
+ * 2.2.2.4 "LMv2_RESPONSE" in [MS-NLMP]) in addition to ClientChallenge
+ * (only in 2.2.2.4 "LMv2_RESPONSE")?
+ *
+ * XXX - should we also dissect the fields of an NtChallengeResponse
+ * (hf_ntlmssp_auth_ntresponse)?
+ *
+ * XXX - should we warn if the blob is too *small*?
+ */
+ if (blob_hf == hf_ntlmssp_auth_lmresponse) {
+ /*
+ * LMChallengeResponse. It's either 2.2.2.3 "LM_RESPONSE" or
+ * 2.2.2.4 "LMv2_RESPONSE", in [MS-NLMP].
+ *
+ * XXX - should we have a field for Response as well as
+ * ClientChallenge?
+ */
+ if (tvb_memeql(tvb, blob_offset+8, (const guint8*)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", NTLMSSP_KEY_LEN) == 0) {
+ /*
+ * LMv2_RESPONSE.
+ *
+ * XXX - according to 2.2.2.4 "LMv2_RESPONSE", the ClientChallenge
+ * is at an offset of 16 from the beginning of the blob; it's not
+ * at the beginning of the blob.
+ */
+ proto_tree_add_item (ntlmssp_tree,
+ hf_ntlmssp_ntlm_client_challenge,
+ tvb, blob_offset, 8, ENC_NA);
+ }
+ } else if (blob_hf == hf_ntlmssp_auth_ntresponse) {
+ /*
+ * NTChallengeResponse. It's either 2.2.2.6 "NTLM v1 Response:
+ * NTLM_RESPONSE" or 2.2.2.8 "NTLM v2 Response: NTLMv2_RESPONSE"
+ * in [MS-NLMP].
+ */
+ if (blob_length > 24) {
+ /*
+ * > 24 bytes, so it's "NTLM v2 Response: NTLMv2_RESPONSE".
+ * An NTLMv2_RESPONSE has 16 bytes of Response followed
+ * by an NTLMv2_CLIENT_CHALLENGE; an NTLMv2_CLIENT_CHALLENGE
+ * is at least 32 bytes, so an NTLMv2_RESPONSE is at least
+ * 48 bytes long.
+ */
+ dissect_ntlmv2_response(tvb, pinfo, tree, blob_offset, blob_length);
+ }
+ }
+
+ return offset;
+}
+
+static int * const ntlmssp_negotiate_flags[] = {
+ &hf_ntlmssp_negotiate_flags_80000000,
+ &hf_ntlmssp_negotiate_flags_40000000,
+ &hf_ntlmssp_negotiate_flags_20000000,
+ &hf_ntlmssp_negotiate_flags_10000000,
+ &hf_ntlmssp_negotiate_flags_8000000,
+ &hf_ntlmssp_negotiate_flags_4000000,
+ &hf_ntlmssp_negotiate_flags_2000000,
+ &hf_ntlmssp_negotiate_flags_1000000,
+ &hf_ntlmssp_negotiate_flags_800000,
+ &hf_ntlmssp_negotiate_flags_400000,
+ &hf_ntlmssp_negotiate_flags_200000,
+ &hf_ntlmssp_negotiate_flags_100000,
+ &hf_ntlmssp_negotiate_flags_80000,
+ &hf_ntlmssp_negotiate_flags_40000,
+ &hf_ntlmssp_negotiate_flags_20000,
+ &hf_ntlmssp_negotiate_flags_10000,
+ &hf_ntlmssp_negotiate_flags_8000,
+ &hf_ntlmssp_negotiate_flags_4000,
+ &hf_ntlmssp_negotiate_flags_2000,
+ &hf_ntlmssp_negotiate_flags_1000,
+ &hf_ntlmssp_negotiate_flags_800,
+ &hf_ntlmssp_negotiate_flags_400,
+ &hf_ntlmssp_negotiate_flags_200,
+ &hf_ntlmssp_negotiate_flags_100,
+ &hf_ntlmssp_negotiate_flags_80,
+ &hf_ntlmssp_negotiate_flags_40,
+ &hf_ntlmssp_negotiate_flags_20,
+ &hf_ntlmssp_negotiate_flags_10,
+ &hf_ntlmssp_negotiate_flags_08,
+ &hf_ntlmssp_negotiate_flags_04,
+ &hf_ntlmssp_negotiate_flags_02,
+ &hf_ntlmssp_negotiate_flags_01,
+ NULL
+};
+
+/* Dissect "version" */
+
+/* From MS-NLMP:
+ 0 Major Version Number 1 byte
+ 1 Minor Version Number 1 byte
+ 2 Build Number short(LE)
+ 3 (Reserved) 3 bytes
+ 4 NTLM Current Revision 1 byte
+*/
+
+static int
+dissect_ntlmssp_version(tvbuff_t *tvb, int offset,
+ proto_tree *ntlmssp_tree)
+{
+ if (ntlmssp_tree) {
+ proto_item *tf;
+ proto_tree *version_tree;
+ tf = proto_tree_add_none_format(ntlmssp_tree, hf_ntlmssp_version, tvb, offset, 8,
+ "Version %u.%u (Build %u); NTLM Current Revision %u",
+ tvb_get_guint8(tvb, offset),
+ tvb_get_guint8(tvb, offset+1),
+ tvb_get_letohs(tvb, offset+2),
+ tvb_get_guint8(tvb, offset+7));
+ version_tree = proto_item_add_subtree (tf, ett_ntlmssp_version);
+ proto_tree_add_item(version_tree, hf_ntlmssp_version_major , tvb, offset , 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(version_tree, hf_ntlmssp_version_minor , tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(version_tree, hf_ntlmssp_version_build_number , tvb, offset+2, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(version_tree, hf_ntlmssp_version_ntlm_current_revision, tvb, offset+7, 1, ENC_LITTLE_ENDIAN);
+ }
+ return offset+8;
+}
+
+/* Dissect a NTLM response. This is documented at
+ http://ubiqx.org/cifs/SMB.html#SMB.8, para 2.8.5.3 */
+
+/* Attribute types */
+/*
+ * XXX - the davenport.sourceforge.net document cited above says that a
+ * type of 5 has been seen, "apparently containing the 'parent' DNS
+ * domain for servers in subdomains".
+ * XXX: MS-NLMP info is newer than Davenport info;
+ * The attribute type list and the attribute names below are
+ * based upon MS-NLMP.
+ */
+
+#define NTLM_TARGET_INFO_END 0x0000
+#define NTLM_TARGET_INFO_NB_COMPUTER_NAME 0x0001
+#define NTLM_TARGET_INFO_NB_DOMAIN_NAME 0x0002
+#define NTLM_TARGET_INFO_DNS_COMPUTER_NAME 0x0003
+#define NTLM_TARGET_INFO_DNS_DOMAIN_NAME 0x0004
+#define NTLM_TARGET_INFO_DNS_TREE_NAME 0x0005
+#define NTLM_TARGET_INFO_FLAGS 0x0006
+#define NTLM_TARGET_INFO_TIMESTAMP 0x0007
+#define NTLM_TARGET_INFO_RESTRICTIONS 0x0008
+#define NTLM_TARGET_INFO_TARGET_NAME 0x0009
+#define NTLM_TARGET_INFO_CHANNEL_BINDINGS 0x000A
+
+static const value_string ntlm_name_types[] = {
+ { NTLM_TARGET_INFO_END, "End of list" },
+ { NTLM_TARGET_INFO_NB_COMPUTER_NAME, "NetBIOS computer name" },
+ { NTLM_TARGET_INFO_NB_DOMAIN_NAME, "NetBIOS domain name" },
+ { NTLM_TARGET_INFO_DNS_COMPUTER_NAME, "DNS computer name" },
+ { NTLM_TARGET_INFO_DNS_DOMAIN_NAME, "DNS domain name" },
+ { NTLM_TARGET_INFO_DNS_TREE_NAME, "DNS tree name" },
+ { NTLM_TARGET_INFO_FLAGS, "Flags" },
+ { NTLM_TARGET_INFO_TIMESTAMP, "Timestamp" },
+ { NTLM_TARGET_INFO_RESTRICTIONS, "Restrictions" },
+ { NTLM_TARGET_INFO_TARGET_NAME, "Target Name"},
+ { NTLM_TARGET_INFO_CHANNEL_BINDINGS, "Channel Bindings"},
+ { 0, NULL }
+};
+static value_string_ext ntlm_name_types_ext = VALUE_STRING_EXT_INIT(ntlm_name_types);
+
+/* The following *must* match the order of the list of attribute types */
+/* Assumption: values in the list are a sequence starting with 0 and */
+/* with no gaps allowing a direct access of the array by attribute type */
+static int *ntlmssp_hf_challenge_target_info_hf_ptr_array[] = {
+ &hf_ntlmssp_challenge_target_info_end,
+ &hf_ntlmssp_challenge_target_info_nb_computer_name,
+ &hf_ntlmssp_challenge_target_info_nb_domain_name,
+ &hf_ntlmssp_challenge_target_info_dns_computer_name,
+ &hf_ntlmssp_challenge_target_info_dns_domain_name,
+ &hf_ntlmssp_challenge_target_info_dns_tree_name,
+ &hf_ntlmssp_challenge_target_info_flags,
+ &hf_ntlmssp_challenge_target_info_timestamp,
+ &hf_ntlmssp_challenge_target_info_restrictions,
+ &hf_ntlmssp_challenge_target_info_target_name,
+ &hf_ntlmssp_challenge_target_info_channel_bindings
+};
+
+static int *ntlmssp_hf_ntlmv2_response_hf_ptr_array[] = {
+ &hf_ntlmssp_ntlmv2_response_end,
+ &hf_ntlmssp_ntlmv2_response_nb_computer_name,
+ &hf_ntlmssp_ntlmv2_response_nb_domain_name,
+ &hf_ntlmssp_ntlmv2_response_dns_computer_name,
+ &hf_ntlmssp_ntlmv2_response_dns_domain_name,
+ &hf_ntlmssp_ntlmv2_response_dns_tree_name,
+ &hf_ntlmssp_ntlmv2_response_flags,
+ &hf_ntlmssp_ntlmv2_response_timestamp,
+ &hf_ntlmssp_ntlmv2_response_restrictions,
+ &hf_ntlmssp_ntlmv2_response_target_name,
+ &hf_ntlmssp_ntlmv2_response_channel_bindings
+};
+
+typedef struct _tif {
+ gint *ett;
+ int *hf_item_type;
+ int *hf_item_length;
+ int **hf_attr_array_p;
+} tif_t;
+
+static tif_t ntlmssp_challenge_target_info_tif = {
+ &ett_ntlmssp_challenge_target_info_item,
+ &hf_ntlmssp_challenge_target_info_item_type,
+ &hf_ntlmssp_challenge_target_info_item_len,
+ ntlmssp_hf_challenge_target_info_hf_ptr_array
+};
+
+static tif_t ntlmssp_ntlmv2_response_tif = {
+ &ett_ntlmssp_ntlmv2_response_item,
+ &hf_ntlmssp_ntlmv2_response_item_type,
+ &hf_ntlmssp_ntlmv2_response_item_len,
+ ntlmssp_hf_ntlmv2_response_hf_ptr_array
+};
+
+/** See [MS-NLMP] 2.2.2.1 */
+static int
+dissect_ntlmssp_target_info_list(tvbuff_t *_tvb, packet_info *pinfo, proto_tree *tree,
+ guint32 target_info_offset, guint16 target_info_length,
+ tif_t *tif_p)
+{
+ tvbuff_t *tvb = tvb_new_subset_length(_tvb, target_info_offset, target_info_length);
+ guint32 item_offset = 0;
+ guint16 item_type = ~0;
+
+ /* Now enumerate through the individual items in the list */
+
+ while (tvb_bytes_exist(tvb, item_offset, 4) && (item_type != NTLM_TARGET_INFO_END)) {
+ proto_item *target_info_tf;
+ proto_tree *target_info_tree;
+ guint32 content_offset;
+ guint16 content_length;
+ guint32 type_offset;
+ guint32 len_offset;
+ guint32 item_length;
+ const guint8 *text = NULL;
+
+ int **hf_array_p = tif_p->hf_attr_array_p;
+
+ /* Content type */
+ type_offset = item_offset;
+ item_type = tvb_get_letohs(tvb, type_offset);
+
+ /* Content length */
+ len_offset = type_offset + 2;
+ content_length = tvb_get_letohs(tvb, len_offset);
+
+ /* Content value */
+ content_offset = len_offset + 2;
+ item_length = content_length + 4;
+
+ if (!tvb_bytes_exist(tvb, item_offset, item_length)) {
+ /* Mark the current item and all the rest as invalid */
+ proto_tree_add_expert(tree, pinfo, &ei_ntlmssp_target_info_invalid,
+ tvb, item_offset, target_info_length - item_offset);
+ return target_info_offset + target_info_length;
+ }
+
+ target_info_tree = proto_tree_add_subtree_format(tree, tvb, item_offset, item_length, *tif_p->ett, &target_info_tf,
+ "Attribute: %s", val_to_str_ext(item_type, &ntlm_name_types_ext, "Unknown (%d)"));
+
+ proto_tree_add_item (target_info_tree, *tif_p->hf_item_type, tvb, type_offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item (target_info_tree, *tif_p->hf_item_length, tvb, len_offset, 2, ENC_LITTLE_ENDIAN);
+
+ if (content_length > 0) {
+ switch (item_type) {
+ case NTLM_TARGET_INFO_NB_COMPUTER_NAME:
+ case NTLM_TARGET_INFO_NB_DOMAIN_NAME:
+ case NTLM_TARGET_INFO_DNS_COMPUTER_NAME:
+ case NTLM_TARGET_INFO_DNS_DOMAIN_NAME:
+ case NTLM_TARGET_INFO_DNS_TREE_NAME:
+ case NTLM_TARGET_INFO_TARGET_NAME:
+ proto_tree_add_item_ret_string(target_info_tree, *hf_array_p[item_type], tvb, content_offset, content_length, ENC_UTF_16|ENC_LITTLE_ENDIAN, wmem_packet_scope(), &text);
+ proto_item_append_text(target_info_tf, ": %s", text);
+ break;
+
+ case NTLM_TARGET_INFO_FLAGS:
+ proto_tree_add_item(target_info_tree, *hf_array_p[item_type], tvb, content_offset, content_length, ENC_LITTLE_ENDIAN);
+ break;
+
+ case NTLM_TARGET_INFO_TIMESTAMP:
+ dissect_nt_64bit_time(tvb, target_info_tree, content_offset, *hf_array_p[item_type]);
+ break;
+
+ case NTLM_TARGET_INFO_RESTRICTIONS:
+ case NTLM_TARGET_INFO_CHANNEL_BINDINGS:
+ proto_tree_add_item(target_info_tree, *hf_array_p[item_type], tvb, content_offset, content_length, ENC_NA);
+ break;
+
+ default:
+ proto_tree_add_expert(target_info_tree, pinfo, &ei_ntlmssp_target_info_attr,
+ tvb, content_offset, content_length);
+ break;
+ }
+ }
+
+ item_offset += item_length;
+ }
+
+ return target_info_offset + item_offset;
+}
+
+/** See [MS-NLMP] 3.3.2 */
+int
+dissect_ntlmv2_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int len)
+{
+ proto_item *ntlmv2_item = NULL;
+ proto_tree *ntlmv2_tree = NULL;
+ const int orig_offset = offset;
+
+ /* XXX - make sure we don't go past len? */
+ if (tree) {
+ ntlmv2_item = proto_tree_add_item(
+ tree, hf_ntlmssp_ntlmv2_response, tvb,
+ offset, len, ENC_NA);
+ ntlmv2_tree = proto_item_add_subtree(
+ ntlmv2_item, ett_ntlmssp_ntlmv2_response);
+ }
+
+ proto_tree_add_item(
+ ntlmv2_tree, hf_ntlmssp_ntlmv2_response_ntproofstr, tvb,
+ offset, 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(ntlmv2_tree, hf_ntlmssp_ntlmv2_response_rversion, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item(ntlmv2_tree, hf_ntlmssp_ntlmv2_response_hirversion, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item(ntlmv2_tree, hf_ntlmssp_ntlmv2_response_z, tvb, offset, 6, ENC_NA);
+ offset += 6;
+
+ offset = dissect_nt_64bit_time(
+ tvb, ntlmv2_tree, offset, hf_ntlmssp_ntlmv2_response_time);
+ proto_tree_add_item(
+ ntlmv2_tree, hf_ntlmssp_ntlmv2_response_chal, tvb,
+ offset, 8, ENC_NA);
+ offset += 8;
+
+ proto_tree_add_item(ntlmv2_tree, hf_ntlmssp_ntlmv2_response_z, tvb, offset, 4, ENC_NA);
+ offset += 4;
+
+ offset = dissect_ntlmssp_target_info_list(tvb, pinfo, ntlmv2_tree, offset, len - (offset - orig_offset), &ntlmssp_ntlmv2_response_tif);
+
+ if ((offset - orig_offset) < len) {
+ proto_tree_add_item(ntlmv2_tree, hf_ntlmssp_ntlmv2_response_pad, tvb, offset, len - (offset - orig_offset), ENC_NA);
+ }
+
+ return offset+len;
+}
+
+/* tapping into ntlmssph not yet implemented */
+static int
+dissect_ntlmssp_negotiate (tvbuff_t *tvb, int offset, proto_tree *ntlmssp_tree, ntlmssp_header_t *ntlmssph _U_)
+{
+ guint32 negotiate_flags;
+ int data_start;
+ int data_end;
+ int item_start;
+ int item_end;
+
+ /* NTLMSSP Negotiate Flags */
+ negotiate_flags = tvb_get_letohl (tvb, offset);
+ proto_tree_add_bitmask(ntlmssp_tree, tvb, offset, hf_ntlmssp_negotiate_flags, ett_ntlmssp_negotiate_flags, ntlmssp_negotiate_flags, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ /*
+ * XXX - the davenport document says that these might not be
+ * sent at all, presumably meaning the length of the message
+ * isn't enough to contain them.
+ */
+ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree, FALSE,
+ hf_ntlmssp_negotiate_domain,
+ &data_start, &data_end, NULL);
+
+ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree, FALSE,
+ hf_ntlmssp_negotiate_workstation,
+ &item_start, &item_end, NULL);
+ data_start = MIN(data_start, item_start);
+ data_end = MAX(data_end, item_end);
+
+ /* If there are more bytes before the data block dissect a version field
+ if NTLMSSP_NEGOTIATE_VERSION is set in the flags (see MS-NLMP) */
+ if (offset < data_start) {
+ if (negotiate_flags & NTLMSSP_NEGOTIATE_VERSION)
+ dissect_ntlmssp_version(tvb, offset, ntlmssp_tree);
+ }
+ return data_end;
+}
+
+
+static int
+dissect_ntlmssp_challenge_target_info_blob (packet_info *pinfo, tvbuff_t *tvb, int offset,
+ proto_tree *ntlmssp_tree,
+ int *end)
+{
+ guint16 challenge_target_info_length = tvb_get_letohs(tvb, offset);
+ guint16 challenge_target_info_maxlen = tvb_get_letohs(tvb, offset+2);
+ guint32 challenge_target_info_offset = tvb_get_letohl(tvb, offset+4);
+ proto_item *tf = NULL;
+ proto_tree *challenge_target_info_tree = NULL;
+
+ /* the target info list is just a blob */
+ if (0 == challenge_target_info_length) {
+ *end = (challenge_target_info_offset > ((guint)offset)+8 ? challenge_target_info_offset : ((guint)offset)+8);
+ proto_tree_add_none_format(ntlmssp_tree, hf_ntlmssp_challenge_target_info, tvb, offset, 8,
+ "Target Info List: Empty");
+ return offset+8;
+ }
+
+ if (ntlmssp_tree) {
+ tf = proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_challenge_target_info, tvb,
+ challenge_target_info_offset, challenge_target_info_length, ENC_NA);
+ challenge_target_info_tree = proto_item_add_subtree(tf, ett_ntlmssp_challenge_target_info);
+ }
+ proto_tree_add_uint(challenge_target_info_tree, hf_ntlmssp_challenge_target_info_len,
+ tvb, offset, 2, challenge_target_info_length);
+ offset += 2;
+ proto_tree_add_uint(challenge_target_info_tree, hf_ntlmssp_challenge_target_info_maxlen,
+ tvb, offset, 2, challenge_target_info_maxlen);
+ offset += 2;
+ proto_tree_add_uint(challenge_target_info_tree, hf_ntlmssp_challenge_target_info_offset,
+ tvb, offset, 4, challenge_target_info_offset);
+ offset += 4;
+
+ dissect_ntlmssp_target_info_list(tvb, pinfo, challenge_target_info_tree,
+ challenge_target_info_offset, challenge_target_info_length,
+ &ntlmssp_challenge_target_info_tif);
+
+ *end = challenge_target_info_offset + challenge_target_info_length;
+ return offset;
+}
+
+/* tapping into ntlmssph not yet implemented */
+static int
+dissect_ntlmssp_challenge (tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *ntlmssp_tree, ntlmssp_header_t *ntlmssph _U_)
+{
+ guint32 negotiate_flags = 0;
+ int item_start, item_end;
+ int data_start, data_end; /* MIN and MAX seen */
+ guint8 clientkey[NTLMSSP_KEY_LEN]; /* NTLMSSP cipher key for client */
+ guint8 serverkey[NTLMSSP_KEY_LEN]; /* NTLMSSP cipher key for server*/
+ ntlmssp_info *conv_ntlmssp_info = NULL;
+ conversation_t *conversation;
+ gboolean unicode_strings = FALSE;
+ guint8 tmp[8];
+ guint8 sspkey[NTLMSSP_KEY_LEN]; /* NTLMSSP cipher key */
+ int ssp_key_len; /* Either 8 or 16 (40 bit or 128) */
+
+ /*
+ * Use the negotiate flags in this message, if they're present
+ * in the capture, to determine whether strings are Unicode or
+ * not.
+ *
+ * offset points at TargetNameFields; skip past it.
+ */
+ if (tvb_bytes_exist(tvb, offset+8, 4)) {
+ negotiate_flags = tvb_get_letohl (tvb, offset+8);
+ if (negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE)
+ unicode_strings = TRUE;
+ }
+
+ /* Target name */
+ /*
+ * XXX - the davenport document (and MS-NLMP) calls this "Target Name",
+ * presumably because non-domain targets are supported.
+ * XXX - Original name "domain" changed to "target_name" to match MS-NLMP
+ */
+ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree, unicode_strings,
+ hf_ntlmssp_challenge_target_name,
+ &item_start, &item_end, NULL);
+ data_start = item_start;
+ data_end = item_end;
+
+ /* NTLMSSP Negotiate Flags */
+ proto_tree_add_bitmask(ntlmssp_tree, tvb, offset, hf_ntlmssp_negotiate_flags, ett_ntlmssp_negotiate_flags, ntlmssp_negotiate_flags, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ /* NTLMSSP NT Lan Manager Challenge */
+ proto_tree_add_item (ntlmssp_tree,
+ hf_ntlmssp_ntlm_server_challenge,
+ tvb, offset, 8, ENC_NA);
+
+ /*
+ * Store the flags and the RC4 state information with the conversation,
+ * as they're needed in order to dissect subsequent messages.
+ */
+ conversation = find_or_create_conversation(pinfo);
+
+ tvb_memcpy(tvb, tmp, offset, 8); /* challenge */
+ /* We can face more than one NTLM exchange over the same couple of IP and ports ...*/
+ conv_ntlmssp_info = (ntlmssp_info *)conversation_get_proto_data(conversation, proto_ntlmssp);
+ /* XXX: The following code is (re)executed every time a particular frame is dissected
+ * (in whatever order). Thus it seems to me that "multiple exchanges" might not be
+ * handled well depending on the order that frames are visited after the initial dissection.
+ */
+ if (!conv_ntlmssp_info || memcmp(tmp, conv_ntlmssp_info->server_challenge, 8) != 0) {
+ conv_ntlmssp_info = wmem_new0(wmem_file_scope(), ntlmssp_info);
+ wmem_register_callback(wmem_file_scope(), ntlmssp_sessions_destroy_cb, conv_ntlmssp_info);
+ /* Insert the flags into the conversation */
+ conv_ntlmssp_info->flags = negotiate_flags;
+ conv_ntlmssp_info->saw_challenge = TRUE;
+ /* Insert the RC4 state information into the conversation */
+ tvb_memcpy(tvb, conv_ntlmssp_info->server_challenge, offset, 8);
+ /* Between the challenge and the user provided password, we can build the
+ NTLMSSP key and initialize the cipher if we are not in EXTENDED SESSION SECURITY
+ in this case we need the client challenge as well*/
+ /* BTW this is true just if we are in LM Authentication if not the logic is a bit different.
+ * Right now it's not very clear what is LM Authentication it __seems__ to be when
+ * NEGOTIATE NT ONLY is not set and NEGOTIATE EXTENDED SESSION SECURITY is not set as well*/
+ if (!(conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY))
+ {
+ conv_ntlmssp_info->rc4_state_initialized = FALSE;
+ /* XXX - Make sure there is 24 bytes for the key */
+ conv_ntlmssp_info->ntlm_response.contents = (guint8 *)wmem_alloc0(wmem_file_scope(), 24);
+ conv_ntlmssp_info->lm_response.contents = (guint8 *)wmem_alloc0(wmem_file_scope(), 24);
+
+ create_ntlmssp_v1_key(conv_ntlmssp_info->server_challenge,
+ NULL, sspkey, NULL, conv_ntlmssp_info->flags,
+ conv_ntlmssp_info->ntlm_response.contents,
+ conv_ntlmssp_info->lm_response.contents,
+ ntlmssph, pinfo, ntlmssp_tree);
+ if (memcmp(sspkey, gbl_zeros, NTLMSSP_KEY_LEN) != 0) {
+ get_sealing_rc4key(sspkey, conv_ntlmssp_info->flags, &ssp_key_len, clientkey, serverkey);
+ if (!gcry_cipher_open(&conv_ntlmssp_info->rc4_handle_client, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ if (gcry_cipher_setkey(conv_ntlmssp_info->rc4_handle_client, sspkey, ssp_key_len)) {
+ gcry_cipher_close(conv_ntlmssp_info->rc4_handle_client);
+ conv_ntlmssp_info->rc4_handle_client = NULL;
+ }
+ }
+ if (!gcry_cipher_open(&conv_ntlmssp_info->rc4_handle_server, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ if (gcry_cipher_setkey(conv_ntlmssp_info->rc4_handle_server, sspkey, ssp_key_len)) {
+ gcry_cipher_close(conv_ntlmssp_info->rc4_handle_server);
+ conv_ntlmssp_info->rc4_handle_server = NULL;
+ }
+ }
+ if (conv_ntlmssp_info->rc4_handle_client && conv_ntlmssp_info->rc4_handle_server) {
+ conv_ntlmssp_info->server_dest_port = pinfo->destport;
+ conv_ntlmssp_info->rc4_state_initialized = TRUE;
+ }
+ }
+ }
+ conversation_add_proto_data(conversation, proto_ntlmssp, conv_ntlmssp_info);
+ }
+ offset += 8;
+
+ /* If no more bytes (ie: no "reserved", ...) before start of data block, then return */
+ /* XXX: According to Davenport "This form is seen in older Win9x-based systems" */
+ /* Also: I've seen a capture with an HTTP CONNECT proxy-authentication */
+ /* message wherein the challenge from the proxy has this form. */
+ if (offset >= data_start) {
+ return data_end;
+ }
+
+ /* Reserved (function not completely known) */
+ /*
+ * XXX - SSP key? The davenport document says
+ *
+ * The context field is typically populated when Negotiate Local
+ * Call is set. It contains an SSPI context handle, which allows
+ * the client to "short-circuit" authentication and effectively
+ * circumvent responding to the challenge. Physically, the context
+ * is two long values. This is covered in greater detail later,
+ * in the "Local Authentication" section.
+ *
+ * It also says that that information may be omitted.
+ */
+ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_reserved,
+ tvb, offset, 8, ENC_NA);
+ offset += 8;
+
+ /*
+ * The presence or absence of this field is not obviously correlated
+ * with any flags in the previous NEGOTIATE message or in this
+ * message (other than the "Workstation Supplied" and "Domain
+ * Supplied" flags in the NEGOTIATE message, at least in the capture
+ * I've seen - but those also correlate with the presence of workstation
+ * and domain name fields, so it doesn't seem to make sense that they
+ * actually *indicate* whether the subsequent CHALLENGE has an
+ * address list).
+ */
+ if (offset < data_start) {
+ offset = dissect_ntlmssp_challenge_target_info_blob(pinfo, tvb, offset, ntlmssp_tree, &item_end);
+ /* XXX: This code assumes that the address list in the data block */
+ /* is always after the target name. Is this OK ? */
+ data_end = MAX(data_end, item_end);
+ }
+
+ /* If there are more bytes before the data block dissect a version field
+ if NTLMSSP_NEGOTIATE_VERSION is set in the flags (see MS-NLMP) */
+ if (offset < data_start) {
+ if (negotiate_flags & NTLMSSP_NEGOTIATE_VERSION)
+ offset = dissect_ntlmssp_version(tvb, offset, ntlmssp_tree);
+ }
+
+ return MAX(offset, data_end);
+}
+
+static int
+dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *ntlmssp_tree, ntlmssp_header_t *ntlmssph)
+{
+ int item_start, item_end;
+ int data_start, data_end = 0;
+ gboolean have_negotiate_flags = FALSE;
+ guint32 negotiate_flags;
+ guint8 sspkey[NTLMSSP_KEY_LEN]; /* exported session key */
+ guint8 clientkey[NTLMSSP_KEY_LEN]; /* NTLMSSP cipher key for client */
+ guint8 serverkey[NTLMSSP_KEY_LEN]; /* NTLMSSP cipher key for server*/
+ guint8 encryptedsessionkey[NTLMSSP_KEY_LEN];
+ ntlmssp_blob sessionblob;
+ gboolean unicode_strings = FALSE;
+ ntlmssp_info *conv_ntlmssp_info;
+ conversation_t *conversation;
+ int ssp_key_len;
+
+ conv_ntlmssp_info = (ntlmssp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_CONV_INFO_KEY);
+ if (conv_ntlmssp_info == NULL) {
+ /*
+ * There isn't any. Is there any from this conversation? If so,
+ * it means this is the first time we've dissected this frame, so
+ * we should give it flag info.
+ */
+ /* XXX: Create conv_ntlmssp_info & etc if no previous CHALLENGE seen */
+ /* so we'll have a place to store flags. */
+ /* This is a bit brute-force but looks like it will be OK. */
+ conversation = find_or_create_conversation(pinfo);
+ conv_ntlmssp_info = (ntlmssp_info *)conversation_get_proto_data(conversation, proto_ntlmssp);
+ if (conv_ntlmssp_info == NULL) {
+ conv_ntlmssp_info = wmem_new0(wmem_file_scope(), ntlmssp_info);
+ wmem_register_callback(wmem_file_scope(), ntlmssp_sessions_destroy_cb, conv_ntlmssp_info);
+ conversation_add_proto_data(conversation, proto_ntlmssp, conv_ntlmssp_info);
+ }
+ /* XXX: The *conv_ntlmssp_info struct attached to the frame is the
+ same as the one attached to the conversation. That is: *both* point to
+ the exact same struct in memory. Is this what is indended ? */
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_CONV_INFO_KEY, conv_ntlmssp_info);
+ }
+
+ /*
+ * Get flag info from the original negotiate message, if any.
+ * This is because the flag information is sometimes missing from
+ * the AUTHENTICATE message, so we can't figure out whether
+ * strings are Unicode or not by looking at *our* flags.
+ *
+ * MS-NLMP says:
+ *
+ * In 2.2.1.1 NEGOTIATE_MESSAGE:
+ *
+ * NegotiateFlags (4 bytes): A NEGOTIATE structure that contains a set
+ * of flags, as defined in section 2.2.2.5. The client sets flags to
+ * indicate options it supports.
+ *
+ * In 2.2.1.2 CHALLENGE_MESSAGE:
+ *
+ * NegotiateFlags (4 bytes): A NEGOTIATE structure that contains a set
+ * of flags, as defined by section 2.2.2.5. The server sets flags to
+ * indicate options it supports or, if there has been a NEGOTIATE_MESSAGE
+ * (section 2.2.1.1), the choices it has made from the options offered
+ * by the client.
+ *
+ * In 2.2.1.3 AUTHENTICATE_MESSAGE:
+ *
+ * NegotiateFlags (4 bytes): In connectionless mode, a NEGOTIATE
+ * structure that contains a set of flags (section 2.2.2.5) and
+ * represents the conclusion of negotiation--the choices the client
+ * has made from the options the server offered in the CHALLENGE_MESSAGE.
+ * In connection-oriented mode, a NEGOTIATE structure that contains the
+ * set of bit flags (section 2.2.2.5) negotiated in the previous messages.
+ *
+ * As 1.3.1 NTLM Authentication Call Flow indicates, in connectionless
+ * mode, there's no NEGOTIATE_MESSAGE, just a CHALLENGE_MESSAGE and
+ * an AUTHENTICATE_MESSAGE.
+ *
+ * So, for connectionless mode, with no NEGOTIATE_MESSAGE, the flags
+ * that are the result of negotiation are in the AUTHENTICATE_MESSAGE;
+ * only at the time the AUTHENTICATE_MESSAGE is sent does the client
+ * know what the server is offering, so, at that point, it can indicate
+ * to the server which of those it supports, with the final result
+ * specifying the capabilities offered by the server that are also
+ * supported by the client.
+ *
+ * For connection-oriented mode, at the time of the CHALLENGE_MESSAGE,
+ * the server knows what capabilities the client supports, as those
+ * we specified in the NEGOTIATE_MESSAGE, so it returns the set of
+ * capabilities, from the set that the client supports, that it also
+ * supports, so the CHALLENGE_MESSAGE contains the final result. The
+ * AUTHENTICATE_MESSAGE "contains the set of bit flags ... negotiated
+ * in the previous messages", so it should contain the same set of
+ * bit flags that were in the CHALLENGE_MESSAGE.
+ *
+ * So we use the flags in this message, the AUTHENTICATE_MESSAGE, if
+ * they're present; if this is connectionless mode, the flags in the
+ * CHALLENGE_MESSAGE aren't sufficient, as they don't indicate what
+ * the client supports, and if this is connection-oriented mode, the
+ * flags here should match what's in the CHALLENGE_MESSAGE.
+ *
+ * The flags might be missing from this message; the message could
+ * have been cut short by the snapshot length, and even if it's not,
+ * some older protocol implementations omit it. If they're missing,
+ * we fall back on what's in the CHALLENGE_MESSAGE.
+ *
+ * XXX: I've seen a capture which does an HTTP CONNECT which:
+ * - has the NEGOTIATE & CHALLENGE messages in one TCP connection;
+ * - has the AUTHENTICATE message in a second TCP connection;
+ * (The authentication aparently succeeded).
+ * For that case, in order to get the flags from the CHALLENGE_MESSAGE,
+ * we'd somehow have to manage NTLMSSP exchanges that cross TCP
+ * connection boundaries.
+ *
+ * offset points at LmChallengeResponseFields; skip past
+ * LmChallengeResponseFields, NtChallengeResponseFields,
+ * DomainNameFields, UserNameFields, WorkstationFields,
+ * and EncryptedRandomSessionKeyFields.
+ */
+ if (tvb_bytes_exist(tvb, offset+8+8+8+8+8+8, 4)) {
+ /*
+ * See where the Lan Manager response's blob begins;
+ * the data area starts at, or before, that location.
+ */
+ data_start = tvb_get_letohl(tvb, offset+4);
+
+ /*
+ * See where the NTLM response's blob begins; the data area
+ * starts at, or before, that location.
+ */
+ item_start = tvb_get_letohl(tvb, offset+8+4);
+ data_start = MIN(data_start, item_start);
+
+ /*
+ * See where the domain name's blob begins; the data area
+ * starts at, or before, that location.
+ */
+ item_start = tvb_get_letohl(tvb, offset+8+8+4);
+ data_start = MIN(data_start, item_start);
+
+ /*
+ * See where the user name's blob begins; the data area
+ * starts at, or before, that location.
+ */
+ item_start = tvb_get_letohl(tvb, offset+8+8+8+4);
+ data_start = MIN(data_start, item_start);
+
+ /*
+ * See where the host name's blob begins; the data area
+ * starts at, or before, that location.
+ */
+ item_start = tvb_get_letohl(tvb, offset+8+8+8+8+4);
+ data_start = MIN(data_start, item_start);
+
+ /*
+ * See if we have a session key and flags.
+ */
+ if (offset+8+8+8+8+8 < data_start) {
+ /*
+ * We have a session key and flags.
+ */
+ negotiate_flags = tvb_get_letohl (tvb, offset+8+8+8+8+8+8);
+ have_negotiate_flags = TRUE;
+ if (negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE)
+ unicode_strings = TRUE;
+ }
+ }
+ if (!have_negotiate_flags) {
+ /*
+ * The flags from this message aren't present; if we have the
+ * flags from the CHALLENGE message, use them.
+ */
+ if (conv_ntlmssp_info != NULL && conv_ntlmssp_info->saw_challenge) {
+ if (conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_UNICODE)
+ unicode_strings = TRUE;
+ }
+ }
+
+ /*
+ * Sometimes the session key and flags are missing.
+ * Sometimes the session key is present but the flags are missing.
+ * XXX Who stay so ? Reading spec I would rather say the opposite: flags are
+ * always present, session information are always there as well but sometime
+ * session information could be null (in case of no session)
+ * Sometimes they're both present.
+ *
+ * This does not correlate with any flags in the previous CHALLENGE
+ * message, and only correlates with "Negotiate Unicode", "Workstation
+ * Supplied", and "Domain Supplied" in the NEGOTIATE message - but
+ * those don't make sense as flags to use to determine this.
+ *
+ * So we check all of the descriptors to figure out where the data
+ * area begins, and if the session key or the flags would be in the
+ * middle of the data area, we assume the field in question is
+ * missing.
+ *
+ * XXX - Reading Davenport and MS-NLMP: as I see it the possibilities are:
+ * a. No session-key; no flags; no version ("Win9x")
+ * b. Session-key & flags.
+ * c. Session-key, flags & version.
+ * In cases b and c the session key may be "null".
+ *
+ */
+
+ /* Lan Manager response */
+ data_start = tvb_get_letohl(tvb, offset+4);
+ offset = dissect_ntlmssp_blob(tvb, pinfo, ntlmssp_tree, offset,
+ hf_ntlmssp_auth_lmresponse,
+ &item_end,
+ conv_ntlmssp_info == NULL ? NULL :
+ &conv_ntlmssp_info->lm_response);
+ data_end = MAX(data_end, item_end);
+
+ /* NTLM response */
+ item_start = tvb_get_letohl(tvb, offset+4);
+ offset = dissect_ntlmssp_blob(tvb, pinfo, ntlmssp_tree, offset,
+ hf_ntlmssp_auth_ntresponse,
+ &item_end,
+ conv_ntlmssp_info == NULL ? NULL :
+ &conv_ntlmssp_info->ntlm_response);
+ data_start = MIN(data_start, item_start);
+ data_end = MAX(data_end, item_end);
+
+ /* domain name */
+ item_start = tvb_get_letohl(tvb, offset+4);
+ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree,
+ unicode_strings,
+ hf_ntlmssp_auth_domain,
+ &item_start, &item_end, &(ntlmssph->domain_name));
+ /*ntlmssph->domain_name_len = item_end-item_start;*/
+ data_start = MIN(data_start, item_start);
+ data_end = MAX(data_end, item_end);
+
+ /* user name */
+ item_start = tvb_get_letohl(tvb, offset+4);
+ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree,
+ unicode_strings,
+ hf_ntlmssp_auth_username,
+ &item_start, &item_end, &(ntlmssph->acct_name));
+ /*ntlmssph->acct_name_len = item_end-item_start;*/
+ data_start = MIN(data_start, item_start);
+ data_end = MAX(data_end, item_end);
+
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "User: %s\\%s",
+ ntlmssph->domain_name, ntlmssph->acct_name);
+
+ /* hostname */
+ item_start = tvb_get_letohl(tvb, offset+4);
+ offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree,
+ unicode_strings,
+ hf_ntlmssp_auth_hostname,
+ &item_start, &item_end, &(ntlmssph->host_name));
+ data_start = MIN(data_start, item_start);
+ data_end = MAX(data_end, item_end);
+
+ sessionblob.length = 0;
+ if (offset < data_start) {
+ /* Session Key */
+ offset = dissect_ntlmssp_blob(tvb, pinfo, ntlmssp_tree, offset,
+ hf_ntlmssp_auth_sesskey,
+ &item_end, &sessionblob);
+ data_end = MAX(data_end, item_end);
+ }
+
+ if (offset < data_start) {
+ /* NTLMSSP Negotiate Flags */
+ negotiate_flags = tvb_get_letohl (tvb, offset);
+ proto_tree_add_bitmask(ntlmssp_tree, tvb, offset, hf_ntlmssp_negotiate_flags, ett_ntlmssp_negotiate_flags, ntlmssp_negotiate_flags, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ /* If no previous flags seen (ie: no previous CHALLENGE) use flags
+ from the AUTHENTICATE message).
+ Assumption: (flags == 0) means flags not previously seen */
+ if ((conv_ntlmssp_info != NULL) && (conv_ntlmssp_info->flags == 0)) {
+ conv_ntlmssp_info->flags = negotiate_flags;
+ }
+ } else
+ negotiate_flags = 0;
+
+ /* If there are more bytes before the data block dissect a version field
+ if NTLMSSP_NEGOTIATE_VERSION is set in the flags (see MS-NLMP) */
+ if (offset < data_start) {
+ if (negotiate_flags & NTLMSSP_NEGOTIATE_VERSION) {
+ offset = dissect_ntlmssp_version(tvb, offset, ntlmssp_tree);
+ } else {
+ proto_tree_add_item(ntlmssp_tree, hf_ntlmssp_ntlmv2_response_z, tvb, offset, 8, ENC_NA);
+ offset += 8;
+ }
+ }
+
+ /* If there are still more bytes before the data block dissect an MIC (message integrity_code) field */
+ /* (See MS-NLMP) */
+ if (offset < data_start) {
+ proto_tree_add_item(ntlmssp_tree, hf_ntlmssp_message_integrity_code, tvb, offset, 16, ENC_NA);
+ offset += 16;
+ }
+
+ if (sessionblob.length > NTLMSSP_KEY_LEN) {
+ expert_add_info_format(pinfo, NULL, &ei_ntlmssp_blob_len_too_long, "Session blob length too long: %u", sessionblob.length);
+ } else if (sessionblob.length != 0) {
+ memcpy(encryptedsessionkey, sessionblob.contents, sessionblob.length);
+ /* Try to attach to an existing conversation if not then it's useless to try to do so
+ * because we are missing important information (ie. server challenge)
+ */
+ if (conv_ntlmssp_info) {
+ /* If we are in EXTENDED SESSION SECURITY then we can now initialize cipher */
+ if ((conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY))
+ {
+ conv_ntlmssp_info->rc4_state_initialized = FALSE;
+ ntlmssp_create_session_key(pinfo,
+ ntlmssp_tree,
+ ntlmssph,
+ conv_ntlmssp_info->flags,
+ conv_ntlmssp_info->server_challenge,
+ encryptedsessionkey,
+ &conv_ntlmssp_info->ntlm_response,
+ &conv_ntlmssp_info->lm_response);
+ /* ssp is the exported session key */
+ memcpy(sspkey, ntlmssph->session_key, NTLMSSP_KEY_LEN);
+ if (memcmp(sspkey, gbl_zeros, NTLMSSP_KEY_LEN) != 0) {
+ get_sealing_rc4key(sspkey, conv_ntlmssp_info->flags, &ssp_key_len, clientkey, serverkey);
+ get_signing_key((guint8*)&conv_ntlmssp_info->sign_key_server, (guint8*)&conv_ntlmssp_info->sign_key_client, sspkey, ssp_key_len);
+ if (!gcry_cipher_open (&conv_ntlmssp_info->rc4_handle_server, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ if (gcry_cipher_setkey(conv_ntlmssp_info->rc4_handle_server, serverkey, ssp_key_len)) {
+ gcry_cipher_close(conv_ntlmssp_info->rc4_handle_server);
+ conv_ntlmssp_info->rc4_handle_server = NULL;
+ }
+ }
+ if (!gcry_cipher_open (&conv_ntlmssp_info->rc4_handle_client, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ if (gcry_cipher_setkey(conv_ntlmssp_info->rc4_handle_client, clientkey, ssp_key_len)) {
+ gcry_cipher_close(conv_ntlmssp_info->rc4_handle_client);
+ conv_ntlmssp_info->rc4_handle_client = NULL;
+ }
+ }
+ if (conv_ntlmssp_info->rc4_handle_server && conv_ntlmssp_info->rc4_handle_client) {
+ conv_ntlmssp_info->server_dest_port = pinfo->destport;
+ conv_ntlmssp_info->rc4_state_initialized = TRUE;
+ }
+ }
+ }
+ }
+ }
+ return MAX(offset, data_end);
+}
+
+static guint8*
+get_sign_key(packet_info *pinfo, int cryptpeer)
+{
+ conversation_t *conversation;
+ ntlmssp_info *conv_ntlmssp_info;
+
+ conversation = find_conversation_pinfo(pinfo, 0);
+ if (conversation == NULL) {
+ /* We don't have a conversation. In this case, stop processing
+ because we do not have enough info to decrypt the payload */
+ return NULL;
+ }
+ else {
+ /* We have a conversation, check for encryption state */
+ conv_ntlmssp_info = (ntlmssp_info *)conversation_get_proto_data(conversation,
+ proto_ntlmssp);
+ if (conv_ntlmssp_info == NULL) {
+ /* No encryption state tied to the conversation. Therefore, we
+ cannot decrypt the payload */
+ return NULL;
+ }
+ else {
+ /* We have the encryption state in the conversation. So return the
+ crypt state tied to the requested peer
+ */
+ if (cryptpeer == 1) {
+ return (guint8*)&conv_ntlmssp_info->sign_key_client;
+ } else {
+ return (guint8*)&conv_ntlmssp_info->sign_key_server;
+ }
+ }
+ }
+}
+
+/*
+ * Get the encryption state tied to this conversation. cryptpeer indicates
+ * whether to retrieve the client key (1) or the server key (0)
+ */
+static gcry_cipher_hd_t
+get_encrypted_state(packet_info *pinfo, int cryptpeer)
+{
+ conversation_t *conversation;
+ ntlmssp_info *conv_ntlmssp_info;
+
+ conversation = find_conversation_pinfo(pinfo, 0);
+ if (conversation == NULL) {
+ /* We don't have a conversation. In this case, stop processing
+ because we do not have enough info to decrypt the payload */
+ return NULL;
+ }
+ else {
+ /* We have a conversation, check for encryption state */
+ conv_ntlmssp_info = (ntlmssp_info *)conversation_get_proto_data(conversation,
+ proto_ntlmssp);
+ if (conv_ntlmssp_info == NULL) {
+ /* No encryption state tied to the conversation. Therefore, we
+ cannot decrypt the payload */
+ return NULL;
+ }
+ else {
+ /* We have the encryption state in the conversation. So return the
+ crypt state tied to the requested peer
+ */
+ if (cryptpeer == 1) {
+ return conv_ntlmssp_info->rc4_handle_client;
+ } else {
+ return conv_ntlmssp_info->rc4_handle_server;
+ }
+ }
+ }
+}
+
+static tvbuff_t*
+decrypt_data_payload(tvbuff_t *tvb, int offset, guint32 encrypted_block_length,
+ packet_info *pinfo, proto_tree *tree _U_, gpointer key);
+static void
+store_verifier(tvbuff_t *tvb, int offset, guint32 encrypted_block_length, packet_info *pinfo);
+
+static void
+decrypt_verifier(tvbuff_t *tvb, packet_info *pinfo);
+
+static int
+dissect_ntlmssp_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ volatile int offset = 0;
+ proto_tree *volatile ntlmssp_tree = NULL;
+ proto_item *tf = NULL;
+ guint32 length;
+ guint32 encrypted_block_length;
+ guint8 key[NTLMSSP_KEY_LEN];
+ /* the magic ntlm is the identifier of a NTLMSSP packet that's 00 00 00 01 */
+ guint32 ntlm_magic_size = 4;
+ guint32 ntlm_signature_size = 8;
+ guint32 ntlm_seq_size = 4;
+
+ length = tvb_captured_length (tvb);
+ /* signature + seq + real payload */
+ encrypted_block_length = length - ntlm_magic_size;
+
+ if (encrypted_block_length < (ntlm_signature_size + ntlm_seq_size)) {
+ /* Don't know why this would happen, but if it does, don't even bother
+ attempting decryption/dissection */
+ return offset + length;
+ }
+
+ /* Setup a new tree for the NTLMSSP payload */
+ if (tree) {
+ tf = proto_tree_add_item (tree,
+ hf_ntlmssp_verf,
+ tvb, offset, -1, ENC_NA);
+
+ ntlmssp_tree = proto_item_add_subtree (tf,
+ ett_ntlmssp);
+ }
+
+ /*
+ * Catch the ReportedBoundsError exception; the stuff we've been
+ * handed doesn't necessarily run to the end of the packet, it's
+ * an item inside a packet, so if it happens to be malformed (or
+ * we, or a dissector we call, has a bug), so that an exception
+ * is thrown, we want to report the error, but return and let
+ * our caller dissect the rest of the packet.
+ *
+ * If it gets a BoundsError, we can stop, as there's nothing more
+ * in the packet after our blob to see, so we just re-throw the
+ * exception.
+ */
+ TRY {
+ /* Version number */
+ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_vers,
+ tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ /* Encrypted body */
+ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_body,
+ tvb, offset, ntlm_signature_size + ntlm_seq_size, ENC_NA);
+ memset(key, 0, sizeof(key));
+ tvb_memcpy(tvb, key, offset, ntlm_signature_size + ntlm_seq_size);
+ /* Try to decrypt */
+ decrypt_data_payload (tvb, offset+(ntlm_signature_size + ntlm_seq_size), encrypted_block_length-(ntlm_signature_size + ntlm_seq_size), pinfo, ntlmssp_tree, key);
+ store_verifier (tvb, offset, ntlm_signature_size + ntlm_seq_size, pinfo);
+ decrypt_verifier (tvb, pinfo);
+ /* let's try to hook ourselves here */
+
+ offset += 12;
+ } CATCH_NONFATAL_ERRORS {
+ show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
+ } ENDTRY;
+
+ return offset;
+}
+
+static tvbuff_t*
+decrypt_data_payload(tvbuff_t *tvb, int offset, guint32 encrypted_block_length,
+ packet_info *pinfo, proto_tree *tree _U_, gpointer key)
+{
+ tvbuff_t *decr_tvb; /* Used to display decrypted buffer */
+ ntlmssp_packet_info *packet_ntlmssp_info;
+ ntlmssp_packet_info *stored_packet_ntlmssp_info = NULL;
+
+ /* Check to see if we already have state for this packet */
+ packet_ntlmssp_info = (ntlmssp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_PACKET_INFO_KEY);
+ if (packet_ntlmssp_info == NULL) {
+ /* We don't have any packet state, so create one */
+ packet_ntlmssp_info = wmem_new0(wmem_file_scope(), ntlmssp_packet_info);
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_PACKET_INFO_KEY, packet_ntlmssp_info);
+ }
+ if (!packet_ntlmssp_info->payload_decrypted) {
+ conversation_t *conversation;
+ ntlmssp_info *conv_ntlmssp_info;
+
+ /* Pull the challenge info from the conversation */
+ conversation = find_conversation_pinfo(pinfo, 0);
+ if (conversation == NULL) {
+ /* There is no conversation, thus no encryption state */
+ return NULL;
+ }
+
+ conv_ntlmssp_info = (ntlmssp_info *)conversation_get_proto_data(conversation,
+ proto_ntlmssp);
+ if (conv_ntlmssp_info == NULL) {
+ /* There is no NTLMSSP state tied to the conversation */
+ return NULL;
+ }
+ if (!conv_ntlmssp_info->rc4_state_initialized) {
+ /* The crypto sybsystem is not initialized. This means that either
+ the conversation did not include a challenge, or that we do not have the right password */
+ return NULL;
+ }
+ if (key != NULL) {
+ stored_packet_ntlmssp_info = (ntlmssp_packet_info *)g_hash_table_lookup(hash_packet, key);
+ }
+ if (stored_packet_ntlmssp_info != NULL && stored_packet_ntlmssp_info->payload_decrypted == TRUE) {
+ /* Mat TBD (stderr, "Found a already decrypted packet\n");*/
+ memcpy(packet_ntlmssp_info, stored_packet_ntlmssp_info, sizeof(ntlmssp_packet_info));
+ /* Mat TBD printnbyte(packet_ntlmssp_info->decrypted_payload, encrypted_block_length, "Data: ", "\n");*/
+ }
+ else {
+ gcry_cipher_hd_t rc4_handle;
+ gcry_cipher_hd_t rc4_handle_peer;
+
+ /* Get the pair of RC4 state structures. One is used for to decrypt the
+ payload. The other is used to re-encrypt the payload to represent
+ the peer */
+ if (conv_ntlmssp_info->server_dest_port == pinfo->destport) {
+ /* client */
+ rc4_handle = get_encrypted_state(pinfo, 1);
+ rc4_handle_peer = get_encrypted_state(pinfo, 0);
+ } else {
+ /* server */
+ rc4_handle = get_encrypted_state(pinfo, 0);
+ rc4_handle_peer = get_encrypted_state(pinfo, 1);
+ }
+
+ if (rc4_handle == NULL) {
+ /* There is no encryption state, so we cannot decrypt */
+ return NULL;
+ }
+
+ /* Store the decrypted contents in the packet state struct
+ (of course at this point, they aren't decrypted yet) */
+ packet_ntlmssp_info->decrypted_payload = (guint8 *)tvb_memdup(wmem_file_scope(), tvb, offset,
+ encrypted_block_length);
+ packet_ntlmssp_info->payload_len = encrypted_block_length;
+ decrypted_payloads = g_slist_prepend(decrypted_payloads,
+ packet_ntlmssp_info->decrypted_payload);
+ if (key != NULL) {
+ g_hash_table_insert(hash_packet, key, packet_ntlmssp_info);
+ }
+
+ /* Do the decryption of the payload */
+ gcry_cipher_decrypt(rc4_handle, packet_ntlmssp_info->decrypted_payload, encrypted_block_length, NULL, 0);
+
+ /* decrypt the verifier */
+ /*printnchar(packet_ntlmssp_info->decrypted_payload, encrypted_block_length, "data: ", "\n");*/
+ /* We setup a temporary buffer so we can re-encrypt the payload after
+ decryption. This is to update the opposite peer's RC4 state
+ it's useful when we have only one key for both conversation
+ in case of KEY_EXCH we have independent key so this is not needed*/
+ if (!(NTLMSSP_NEGOTIATE_KEY_EXCH & conv_ntlmssp_info->flags)) {
+ guint8 *peer_block;
+ peer_block = (guint8 *)wmem_memdup(wmem_packet_scope(), packet_ntlmssp_info->decrypted_payload, encrypted_block_length);
+ gcry_cipher_decrypt(rc4_handle_peer, peer_block, encrypted_block_length, NULL, 0);
+ }
+
+ packet_ntlmssp_info->payload_decrypted = TRUE;
+ }
+ }
+
+ /* Show the decrypted buffer in a new window */
+ decr_tvb = tvb_new_child_real_data(tvb, packet_ntlmssp_info->decrypted_payload,
+ encrypted_block_length,
+ encrypted_block_length);
+
+ add_new_data_source(pinfo, decr_tvb,
+ "Decrypted data");
+ return decr_tvb;
+}
+
+static int
+dissect_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ volatile int offset = 0;
+ proto_tree *volatile ntlmssp_tree = NULL;
+ proto_item *tf, *type_item;
+ ntlmssp_header_t *ntlmssph;
+
+ /* Check if it is a signing signature */
+ if (tvb_bytes_exist(tvb, offset, 16) &&
+ tvb_reported_length_remaining(tvb, offset) == 16 &&
+ tvb_get_guint8(tvb, offset) == 0x01)
+ {
+ tvbuff_t *verf_tvb = tvb_new_subset_length(tvb, offset, 16);
+ offset += dissect_ntlmssp_verf(verf_tvb, pinfo, tree, NULL);
+ return offset;
+ }
+
+ ntlmssph = wmem_new(wmem_packet_scope(), ntlmssp_header_t);
+ ntlmssph->type = 0;
+ ntlmssph->domain_name = NULL;
+ ntlmssph->acct_name = NULL;
+ ntlmssph->host_name = NULL;
+ memset(ntlmssph->session_key, 0, NTLMSSP_KEY_LEN);
+
+ /* Setup a new tree for the NTLMSSP payload */
+ tf = proto_tree_add_item (tree,
+ proto_ntlmssp,
+ tvb, offset, -1, ENC_NA);
+
+ ntlmssp_tree = proto_item_add_subtree (tf, ett_ntlmssp);
+
+ /*
+ * Catch the ReportedBoundsError exception; the stuff we've been
+ * handed doesn't necessarily run to the end of the packet, it's
+ * an item inside a packet, so if it happens to be malformed (or
+ * we, or a dissector we call, has a bug), so that an exception
+ * is thrown, we want to report the error, but return and let
+ * our caller dissect the rest of the packet.
+ *
+ * If it gets a BoundsError, we can stop, as there's nothing more
+ * in the packet after our blob to see, so we just re-throw the
+ * exception.
+ */
+ TRY {
+ /* NTLMSSP constant */
+ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_auth,
+ tvb, offset, 8, ENC_ASCII);
+ offset += 8;
+
+ /* NTLMSSP Message Type */
+ type_item = proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_message_type,
+ tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ ntlmssph->type = tvb_get_letohl (tvb, offset);
+ offset += 4;
+
+ col_append_sep_str(pinfo->cinfo, COL_INFO, ", ",
+ val_to_str_const(ntlmssph->type,
+ ntlmssp_message_types,
+ "Unknown NTLMSSP message type"));
+
+ /* Call the appropriate dissector based on the Message Type */
+ switch (ntlmssph->type) {
+
+ case NTLMSSP_NEGOTIATE:
+ dissect_ntlmssp_negotiate (tvb, offset, ntlmssp_tree, ntlmssph);
+ break;
+
+ case NTLMSSP_CHALLENGE:
+ dissect_ntlmssp_challenge (tvb, pinfo, offset, ntlmssp_tree, ntlmssph);
+ break;
+
+ case NTLMSSP_AUTH:
+ dissect_ntlmssp_auth (tvb, pinfo, offset, ntlmssp_tree, ntlmssph);
+ break;
+
+ default:
+ /* Unrecognized message type */
+ expert_add_info(pinfo, type_item, &ei_ntlmssp_message_type);
+ break;
+ }
+ } CATCH_NONFATAL_ERRORS {
+
+ show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
+ } ENDTRY;
+
+ tap_queue_packet(ntlmssp_tap, pinfo, ntlmssph);
+ return tvb_captured_length(tvb);
+}
+
+static void
+store_verifier(tvbuff_t *tvb, int offset, guint32 encrypted_block_length, packet_info *pinfo)
+{
+ ntlmssp_packet_info *packet_ntlmssp_info;
+
+ packet_ntlmssp_info = (ntlmssp_packet_info*)p_get_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_PACKET_INFO_KEY);
+ if (packet_ntlmssp_info == NULL) {
+ /* We don't have any packet state, so create one */
+ packet_ntlmssp_info = wmem_new0(wmem_file_scope(), ntlmssp_packet_info);
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_PACKET_INFO_KEY, packet_ntlmssp_info);
+ }
+
+ if (!packet_ntlmssp_info->verifier_decrypted) {
+ /* Store all necessary info for later decryption */
+ packet_ntlmssp_info->verifier_offset = offset;
+ packet_ntlmssp_info->verifier_block_length = encrypted_block_length;
+ /* Setup the buffer to decrypt to */
+ tvb_memcpy(tvb, packet_ntlmssp_info->verifier,
+ offset, MIN(encrypted_block_length, sizeof(packet_ntlmssp_info->verifier)));
+ }
+}
+
+/*
+ * See page 45 of "DCE/RPC over SMB" by Luke Kenneth Casson Leighton.
+ */
+static void
+decrypt_verifier(tvbuff_t *tvb, packet_info *pinfo)
+{
+ proto_tree *decr_tree;
+ conversation_t *conversation;
+ guint8* sign_key;
+ gcry_cipher_hd_t rc4_handle;
+ gcry_cipher_hd_t rc4_handle_peer;
+ tvbuff_t *decr_tvb; /* Used to display decrypted buffer */
+ guint8 *peer_block;
+ guint8 *check_buf;
+ guint8 calculated_md5[NTLMSSP_KEY_LEN];
+ ntlmssp_info *conv_ntlmssp_info;
+ ntlmssp_packet_info *packet_ntlmssp_info;
+ int decrypted_offset = 0;
+ int sequence = 0;
+
+ packet_ntlmssp_info = (ntlmssp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ntlmssp, NTLMSSP_PACKET_INFO_KEY);
+ if (packet_ntlmssp_info == NULL) {
+ /* We don't have data for this packet */
+ return;
+ }
+ conversation = find_conversation_pinfo(pinfo, 0);
+ if (conversation == NULL) {
+ /* There is no conversation, thus no encryption state */
+ return;
+ }
+ conv_ntlmssp_info = (ntlmssp_info *)conversation_get_proto_data(conversation,
+ proto_ntlmssp);
+ if (conv_ntlmssp_info == NULL) {
+ /* There is no NTLMSSP state tied to the conversation */
+ return;
+ }
+
+ if (!packet_ntlmssp_info->verifier_decrypted) {
+ if (!conv_ntlmssp_info->rc4_state_initialized) {
+ /* The crypto subsystem is not initialized. This means that either
+ the conversation did not include a challenge, or we are doing
+ something other than NTLMSSP v1 */
+ return;
+ }
+ if (conv_ntlmssp_info->server_dest_port == pinfo->destport) {
+ /* client talk to server */
+ rc4_handle = get_encrypted_state(pinfo, 1);
+ sign_key = get_sign_key(pinfo, 1);
+ rc4_handle_peer = get_encrypted_state(pinfo, 0);
+ } else {
+ rc4_handle = get_encrypted_state(pinfo, 0);
+ sign_key = get_sign_key(pinfo, 0);
+ rc4_handle_peer = get_encrypted_state(pinfo, 1);
+ }
+
+ if (rc4_handle == NULL || rc4_handle_peer == NULL) {
+ /* There is no encryption state, so we cannot decrypt */
+ return;
+ }
+
+ /*if (!(NTLMSSP_NEGOTIATE_KEY_EXCH & packet_ntlmssp_info->flags)) {*/
+ if (conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+ if ((NTLMSSP_NEGOTIATE_KEY_EXCH & conv_ntlmssp_info->flags)) {
+ /* The spec says that if we have a key exchange then we have the signature that is encrypted
+ * otherwise it's just a hmac_md5(keysign, concat(message, sequence))[0..7]
+ */
+ if (gcry_cipher_decrypt(rc4_handle, packet_ntlmssp_info->verifier, 8, NULL, 0)) {
+ return;
+ }
+ }
+ /*
+ * Trying to check the HMAC MD5 of the message against the calculated one works great with LDAP payload but
+ * don't with DCE/RPC calls.
+ * TODO Some analysis needs to be done ...
+ */
+ if (sign_key != NULL) {
+ check_buf = (guint8 *)wmem_alloc(wmem_packet_scope(), packet_ntlmssp_info->payload_len+4);
+ tvb_memcpy(tvb, &sequence, packet_ntlmssp_info->verifier_offset+8, 4);
+ memcpy(check_buf, &sequence, 4);
+ memcpy(check_buf+4, packet_ntlmssp_info->decrypted_payload, packet_ntlmssp_info->payload_len);
+ if (ws_hmac_buffer(GCRY_MD_MD5, calculated_md5, check_buf, (int)(packet_ntlmssp_info->payload_len+4), sign_key, NTLMSSP_KEY_LEN)) {
+ return;
+ }
+ /*
+ printnbyte(packet_ntlmssp_info->verifier, 8, "HMAC from packet: ", "\n");
+ printnbyte(calculated_md5, 8, "HMAC : ", "\n");
+ */
+ }
+ }
+ else {
+ /* The packet has a PAD then a checksum then a sequence and they are encoded in this order so we can decrypt all at once */
+ /* Do the actual decryption of the verifier */
+ if (gcry_cipher_decrypt(rc4_handle, packet_ntlmssp_info->verifier, packet_ntlmssp_info->verifier_block_length, NULL, 0)) {
+ return;
+ }
+ }
+
+
+ /* We setup a temporary buffer so we can re-encrypt the payload after
+ decryption. This is to update the opposite peer's RC4 state
+ This is not needed when we just have EXTENDED SESSION SECURITY because the signature is not encrypted
+ and it's also not needed when we have key exchange because server and client have independent keys */
+ if (!(NTLMSSP_NEGOTIATE_KEY_EXCH & conv_ntlmssp_info->flags) && !(NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY & conv_ntlmssp_info->flags)) {
+ peer_block = (guint8 *)wmem_memdup(wmem_packet_scope(), packet_ntlmssp_info->verifier, packet_ntlmssp_info->verifier_block_length);
+ if (gcry_cipher_decrypt(rc4_handle_peer, peer_block, packet_ntlmssp_info->verifier_block_length, NULL, 0)) {
+ return;
+ }
+ }
+
+ /* Mark the packet as decrypted so that subsequent attempts to dissect
+ the packet use the already decrypted payload instead of attempting
+ to decrypt again */
+ packet_ntlmssp_info->verifier_decrypted = TRUE;
+ }
+
+ /* Show the decrypted buffer in a new window */
+ decr_tvb = tvb_new_child_real_data(tvb, packet_ntlmssp_info->verifier,
+ packet_ntlmssp_info->verifier_block_length,
+ packet_ntlmssp_info->verifier_block_length);
+ add_new_data_source(pinfo, decr_tvb,
+ "Decrypted NTLMSSP Verifier");
+
+ /* Show the decrypted payload in the tree */
+ decr_tree = proto_tree_add_subtree_format(NULL, decr_tvb, 0, -1,
+ ett_ntlmssp, NULL,
+ "Decrypted Verifier (%d byte%s)",
+ packet_ntlmssp_info->verifier_block_length,
+ plurality(packet_ntlmssp_info->verifier_block_length, "", "s"));
+
+ if (( conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) {
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_hmacmd5,
+ decr_tvb, decrypted_offset, 8, ENC_NA);
+ decrypted_offset += 8;
+
+ /* Incrementing sequence number of DCE conversation */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_sequence,
+ decr_tvb, decrypted_offset, 4, ENC_NA);
+ }
+ else {
+ /* RANDOM PAD usually it's 0 */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_randompad,
+ decr_tvb, decrypted_offset, 4, ENC_LITTLE_ENDIAN);
+ decrypted_offset += 4;
+
+ /* CRC32 of the DCE fragment data */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_crc32,
+ decr_tvb, decrypted_offset, 4, ENC_LITTLE_ENDIAN);
+ decrypted_offset += 4;
+
+ /* Incrementing sequence number of DCE conversation */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_sequence,
+ decr_tvb, decrypted_offset, 4, ENC_NA);
+ }
+}
+
+/* Used when NTLMSSP is done over DCE/RPC because in this case verifier and real payload are not contiguous*/
+static int
+dissect_ntlmssp_payload_only(tvbuff_t *tvb, packet_info *pinfo, _U_ proto_tree *tree, void *data)
+{
+ volatile int offset = 0;
+ proto_tree *volatile ntlmssp_tree = NULL;
+ guint32 encrypted_block_length;
+ tvbuff_t *volatile decr_tvb;
+ tvbuff_t** ret_decr_tvb = (tvbuff_t**)data;
+
+ if (ret_decr_tvb)
+ *ret_decr_tvb = NULL;
+ /* the magic ntlm is the identifier of a NTLMSSP packet that's 00 00 00 01
+ */
+ encrypted_block_length = tvb_captured_length (tvb);
+ /* signature + seq + real payload */
+
+ /* Setup a new tree for the NTLMSSP payload */
+#if 0
+ if (tree) {
+ tf = proto_tree_add_item (tree,
+ hf_ntlmssp_verf,
+ tvb, offset, -1, ENC_NA);
+
+ ntlmssp_tree = proto_item_add_subtree (tf,
+ ett_ntlmssp);
+ }
+#endif
+ /*
+ * Catch the ReportedBoundsError exception; the stuff we've been
+ * handed doesn't necessarily run to the end of the packet, it's
+ * an item inside a packet, so if it happens to be malformed (or
+ * we, or a dissector we call, has a bug), so that an exception
+ * is thrown, we want to report the error, but return and let
+ * our caller dissect the rest of the packet.
+ *
+ * If it gets a BoundsError, we can stop, as there's nothing more
+ * in the packet after our blob to see, so we just re-throw the
+ * exception.
+ */
+ TRY {
+ /* Version number */
+
+ /* Try to decrypt */
+ decr_tvb = decrypt_data_payload (tvb, offset, encrypted_block_length, pinfo, ntlmssp_tree, NULL);
+ if (ret_decr_tvb)
+ *ret_decr_tvb = decr_tvb;
+ /* let's try to hook ourselves here */
+
+ } CATCH_NONFATAL_ERRORS {
+
+ show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
+ } ENDTRY;
+
+ return offset;
+}
+
+/* Used when NTLMSSP is done over DCE/RPC because in this case verifier and real payload are not contiguous
+ * But in fact this function could be merged with wrap_dissect_ntlmssp_verf because it's only used there
+ */
+static int
+dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ volatile int offset = 0;
+ proto_tree *volatile ntlmssp_tree = NULL;
+ proto_item *tf = NULL;
+ guint32 verifier_length;
+ guint32 encrypted_block_length;
+
+ verifier_length = tvb_captured_length (tvb);
+ encrypted_block_length = verifier_length - 4;
+
+ if (encrypted_block_length < 12) {
+ /* Don't know why this would happen, but if it does, don't even bother
+ attempting decryption/dissection */
+ return offset + verifier_length;
+ }
+
+ /* Setup a new tree for the NTLMSSP payload */
+ if (tree) {
+ tf = proto_tree_add_item (tree,
+ hf_ntlmssp_verf,
+ tvb, offset, -1, ENC_NA);
+
+ ntlmssp_tree = proto_item_add_subtree (tf,
+ ett_ntlmssp);
+ }
+
+ /*
+ * Catch the ReportedBoundsError exception; the stuff we've been
+ * handed doesn't necessarily run to the end of the packet, it's
+ * an item inside a packet, so if it happens to be malformed (or
+ * we, or a dissector we call, has a bug), so that an exception
+ * is thrown, we want to report the error, but return and let
+ * our caller dissect the rest of the packet.
+ *
+ * If it gets a BoundsError, we can stop, as there's nothing more
+ * in the packet after our blob to see, so we just re-throw the
+ * exception.
+ */
+ TRY {
+ /* Version number */
+ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_vers,
+ tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ /* Encrypted body */
+ proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_body,
+ tvb, offset, encrypted_block_length, ENC_NA);
+
+ /* Extract and store the verifier for later decryption */
+ store_verifier (tvb, offset, encrypted_block_length, pinfo);
+ /* let's try to hook ourselves here */
+
+ offset += 12;
+ offset += encrypted_block_length;
+ } CATCH_NONFATAL_ERRORS {
+
+ show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
+ } ENDTRY;
+
+ return offset;
+}
+
+static tvbuff_t *
+wrap_dissect_ntlmssp_payload_only(tvbuff_t *header_tvb _U_,
+ tvbuff_t *payload_tvb,
+ tvbuff_t *trailer_tvb _U_,
+ tvbuff_t *auth_tvb _U_,
+ packet_info *pinfo,
+ dcerpc_auth_info *auth_info _U_)
+{
+ tvbuff_t *decrypted_tvb;
+
+ dissect_ntlmssp_payload_only(payload_tvb, pinfo, NULL, &decrypted_tvb);
+ /* Now the payload is decrypted, we can then decrypt the verifier which was stored earlier */
+ decrypt_verifier(payload_tvb, pinfo);
+ return decrypted_tvb;
+}
+
+static guint
+header_hash(gconstpointer pointer)
+{
+ guint32 crc = ~crc32c_calculate(pointer, NTLMSSP_KEY_LEN, CRC32C_PRELOAD);
+ /* Mat TBD fprintf(stderr, "Val: %u\n", crc);*/
+ return crc;
+}
+
+static gboolean
+header_equal(gconstpointer pointer1, gconstpointer pointer2)
+{
+ if (!memcmp(pointer1, pointer2, 16)) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+static void
+ntlmssp_init_protocol(void)
+{
+ hash_packet = g_hash_table_new(header_hash, header_equal);
+}
+
+static void
+ntlmssp_cleanup_protocol(void)
+{
+ if (decrypted_payloads != NULL) {
+ g_slist_free(decrypted_payloads);
+ decrypted_payloads = NULL;
+ }
+ g_hash_table_destroy(hash_packet);
+}
+
+
+
+static int
+wrap_dissect_ntlmssp(tvbuff_t *tvb, int offset, packet_info *pinfo,
+ proto_tree *tree, dcerpc_info *di _U_, guint8 *drep _U_)
+{
+ tvbuff_t *auth_tvb;
+
+ auth_tvb = tvb_new_subset_remaining(tvb, offset);
+
+ dissect_ntlmssp(auth_tvb, pinfo, tree, NULL);
+
+ return tvb_captured_length_remaining(tvb, offset);
+}
+
+static int
+wrap_dissect_ntlmssp_verf(tvbuff_t *tvb, int offset, packet_info *pinfo,
+ proto_tree *tree, dcerpc_info *di _U_, guint8 *drep _U_)
+{
+ tvbuff_t *auth_tvb;
+
+ auth_tvb = tvb_new_subset_remaining(tvb, offset);
+ return dissect_ntlmssp_verf(auth_tvb, pinfo, tree, NULL);
+}
+
+static dcerpc_auth_subdissector_fns ntlmssp_sign_fns = {
+ wrap_dissect_ntlmssp, /* Bind */
+ wrap_dissect_ntlmssp, /* Bind ACK */
+ wrap_dissect_ntlmssp, /* AUTH3 */
+ wrap_dissect_ntlmssp_verf, /* Request verifier */
+ wrap_dissect_ntlmssp_verf, /* Response verifier */
+ NULL, /* Request data */
+ NULL /* Response data */
+};
+
+static dcerpc_auth_subdissector_fns ntlmssp_seal_fns = {
+ wrap_dissect_ntlmssp, /* Bind */
+ wrap_dissect_ntlmssp, /* Bind ACK */
+ wrap_dissect_ntlmssp, /* AUTH3 */
+ wrap_dissect_ntlmssp_verf, /* Request verifier */
+ wrap_dissect_ntlmssp_verf, /* Response verifier */
+ wrap_dissect_ntlmssp_payload_only, /* Request data */
+ wrap_dissect_ntlmssp_payload_only /* Response data */
+};
+
+static const value_string MSV1_0_CRED_VERSION[] = {
+ { 0x00000000, "MSV1_0_CRED_VERSION" },
+ { 0x00000002, "MSV1_0_CRED_VERSION_V2" },
+ { 0x00000004, "MSV1_0_CRED_VERSION_V3" },
+ { 0xffff0001, "MSV1_0_CRED_VERSION_IUM" },
+ { 0xffff0002, "MSV1_0_CRED_VERSION_REMOTE" },
+ { 0xfffffffe, "MSV1_0_CRED_VERSION_RESERVED_1" },
+ { 0xffffffff, "MSV1_0_CRED_VERSION_INVALID" },
+ { 0, NULL }
+};
+
+#define MSV1_0_CRED_LM_PRESENT 0x00000001
+#define MSV1_0_CRED_NT_PRESENT 0x00000002
+#define MSV1_0_CRED_REMOVED 0x00000004
+#define MSV1_0_CRED_CREDKEY_PRESENT 0x00000008
+#define MSV1_0_CRED_SHA_PRESENT 0x00000010
+
+static int* const MSV1_0_CRED_FLAGS_bits[] = {
+ &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_LM_PRESENT,
+ &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_NT_PRESENT,
+ &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_REMOVED,
+ &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_CREDKEY_PRESENT,
+ &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_SHA_PRESENT,
+ NULL
+};
+
+static const value_string MSV1_0_CREDENTIAL_KEY_TYPE[] = {
+ { 0, "InvalidCredKey" },
+ { 1, "IUMCredKey" },
+ { 2, "DomainUserCredKey" },
+ { 3, "LocalUserCredKey" },
+ { 4, "ExternallySuppliedCredKey" },
+ { 0, NULL }
+};
+
+#define MSV1_0_CREDENTIAL_KEY_LENGTH 20
+
+int
+dissect_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+ proto_item *item;
+ proto_tree *subtree;
+ guint32 EncryptedCredsSize;
+
+ if (tvb_captured_length(tvb) < 36)
+ return offset;
+
+ item = proto_tree_add_item(tree, hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL, tvb,
+ offset, -1, ENC_NA);
+ subtree = proto_item_add_subtree(item, ett_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL);
+
+ proto_tree_add_item(subtree, hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_Version, tvb,
+ offset, 4, ENC_LITTLE_ENDIAN);
+ offset+=4;
+
+ proto_tree_add_bitmask(subtree, tvb, offset,
+ hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_Flags,
+ ett_ntlmssp, MSV1_0_CRED_FLAGS_bits, ENC_LITTLE_ENDIAN);
+ offset+=4;
+
+ proto_tree_add_item(subtree, hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_CredentialKey,
+ tvb, offset, MSV1_0_CREDENTIAL_KEY_LENGTH, ENC_NA);
+ offset+=MSV1_0_CREDENTIAL_KEY_LENGTH;
+
+ proto_tree_add_item(subtree, hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_CredentialKeyType,
+ tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset+=4;
+
+ EncryptedCredsSize = tvb_get_letohl(tvb, offset);
+ proto_tree_add_item(subtree, hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_EncryptedCredsSize,
+ tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset+=4;
+
+ if (EncryptedCredsSize == 0)
+ return offset;
+
+ if (tvb_captured_length(tvb) < (36 + EncryptedCredsSize))
+ return offset;
+
+ proto_tree_add_item(subtree, hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_EncryptedCreds,
+ tvb, offset, EncryptedCredsSize, ENC_NA);
+ offset+=EncryptedCredsSize;
+
+ return offset;
+}
+
+
+void
+proto_register_ntlmssp(void)
+{
+
+ static hf_register_info hf[] = {
+ { &hf_ntlmssp_auth,
+ { "NTLMSSP identifier", "ntlmssp.identifier",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_message_type,
+ { "NTLM Message Type", "ntlmssp.messagetype",
+ FT_UINT32, BASE_HEX, VALS(ntlmssp_message_types), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags,
+ { "Negotiate Flags", "ntlmssp.negotiateflags",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_01,
+ { "Negotiate UNICODE", "ntlmssp.negotiateunicode",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_UNICODE,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_02,
+ { "Negotiate OEM", "ntlmssp.negotiateoem",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_OEM,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_04,
+ { "Request Target", "ntlmssp.requesttarget",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_REQUEST_TARGET,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_08,
+ { "Request 0x00000008", "ntlmssp.unused00000008",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_00000008,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_10,
+ { "Negotiate Sign", "ntlmssp.negotiatesign",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_SIGN,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_20,
+ { "Negotiate Seal", "ntlmssp.negotiateseal",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_SEAL,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_40,
+ { "Negotiate Datagram", "ntlmssp.negotiatedatagram",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_DATAGRAM,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_80,
+ { "Negotiate Lan Manager Key", "ntlmssp.negotiatelmkey",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_LM_KEY,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_100,
+ { "Negotiate 0x00000100", "ntlmssp.unused00000100",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_00000100,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_200,
+ { "Negotiate NTLM key", "ntlmssp.negotiatentlm",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_NTLM,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_400,
+ { "Negotiate 0x00000400", "ntlmssp.unused00000400",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_00000400,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_800,
+ { "Negotiate Anonymous", "ntlmssp.negotiateanonymous",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_ANONYMOUS,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_1000,
+ { "Negotiate OEM Domain Supplied", "ntlmssp.negotiateoemdomainsupplied",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_2000,
+ { "Negotiate OEM Workstation Supplied", "ntlmssp.negotiateoemworkstationsupplied",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_4000,
+ { "Negotiate 0x00004000", "ntlmssp.unused00004000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_00004000,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_8000,
+ { "Negotiate Always Sign", "ntlmssp.negotiatealwayssign",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_ALWAYS_SIGN,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_10000,
+ { "Target Type Domain", "ntlmssp.targettypedomain",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_TARGET_TYPE_DOMAIN,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_20000,
+ { "Target Type Server", "ntlmssp.targettypeserver",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_TARGET_TYPE_SERVER,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_40000,
+ { "Negotiate 0x00040000", "ntlmssp.unused00040000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_00040000,
+ NULL, HFILL }
+ },
+
+/* Negotiate Flags */
+ { &hf_ntlmssp_negotiate_flags_80000,
+ { "Negotiate Extended Session Security", "ntlmssp.negotiateextendedsessionsecurity",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_100000,
+ { "Negotiate Identify", "ntlmssp.negotiateidentify",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_IDENTIFY,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_200000,
+ { "Negotiate 0x00200000", "ntlmssp.unused00200000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_00200000,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_400000,
+ { "Request Non-NT Session Key", "ntlmssp.requestnonntsessionkey",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_REQUEST_NON_NT_SESSION_KEY,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_800000,
+ { "Negotiate Target Info", "ntlmssp.negotiatetargetinfo",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_TARGET_INFO,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_1000000,
+ { "Negotiate 0x01000000", "ntlmssp.unused01000000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_01000000,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_2000000,
+ { "Negotiate Version", "ntlmssp.negotiateversion",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_VERSION,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_4000000,
+ { "Negotiate 0x04000000", "ntlmssp.unused04000000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_04000000,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_8000000,
+ { "Negotiate 0x08000000", "ntlmssp.unused08000000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_08000000,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_10000000,
+ { "Negotiate 0x10000000", "ntlmssp.unused10000000",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_UNUSED_10000000,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_20000000,
+ { "Negotiate 128", "ntlmssp.negotiate128",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_128,
+ "128-bit encryption is supported", HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_40000000,
+ { "Negotiate Key Exchange", "ntlmssp.negotiatekeyexch",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_KEY_EXCH,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_negotiate_flags_80000000,
+ { "Negotiate 56", "ntlmssp.negotiate56",
+ FT_BOOLEAN, 32, TFS (&tfs_set_notset), NTLMSSP_NEGOTIATE_56,
+ "56-bit encryption is supported", HFILL }
+ },
+#if 0
+ { &hf_ntlmssp_negotiate_workstation_strlen,
+ { "Calling workstation name length", "ntlmssp.negotiate.callingworkstation.strlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+#if 0
+ { &hf_ntlmssp_negotiate_workstation_maxlen,
+ { "Calling workstation name max length", "ntlmssp.negotiate.callingworkstation.maxlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+#if 0
+ { &hf_ntlmssp_negotiate_workstation_buffer,
+ { "Calling workstation name buffer", "ntlmssp.negotiate.callingworkstation.buffer",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+ { &hf_ntlmssp_negotiate_workstation,
+ { "Calling workstation name", "ntlmssp.negotiate.callingworkstation",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+#if 0
+ { &hf_ntlmssp_negotiate_domain_strlen,
+ { "Calling workstation domain length", "ntlmssp.negotiate.domain.strlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+#if 0
+ { &hf_ntlmssp_negotiate_domain_maxlen,
+ { "Calling workstation domain max length", "ntlmssp.negotiate.domain.maxlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+#if 0
+ { &hf_ntlmssp_negotiate_domain_buffer,
+ { "Calling workstation domain buffer", "ntlmssp.negotiate.domain.buffer",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+ { &hf_ntlmssp_negotiate_domain,
+ { "Calling workstation domain", "ntlmssp.negotiate.domain",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlm_client_challenge,
+ { "LMv2 Client Challenge", "ntlmssp.ntlmclientchallenge",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "The 8-byte LMv2 challenge message generated by the client", HFILL }
+ },
+ { &hf_ntlmssp_ntlm_server_challenge,
+ { "NTLM Server Challenge", "ntlmssp.ntlmserverchallenge",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_reserved,
+ { "Reserved", "ntlmssp.reserved",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+
+ { &hf_ntlmssp_challenge_target_name,
+ { "Target Name", "ntlmssp.challenge.target_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_auth_domain,
+ { "Domain name", "ntlmssp.auth.domain",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_auth_username,
+ { "User name", "ntlmssp.auth.username",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_auth_hostname,
+ { "Host name", "ntlmssp.auth.hostname",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_auth_lmresponse,
+ { "Lan Manager Response", "ntlmssp.auth.lmresponse",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_auth_ntresponse,
+ { "NTLM Response", "ntlmssp.auth.ntresponse",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_auth_sesskey,
+ { "Session Key", "ntlmssp.auth.sesskey",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_string_len,
+ { "Length", "ntlmssp.string.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_string_maxlen,
+ { "Maxlen", "ntlmssp.string.maxlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_string_offset,
+ { "Offset", "ntlmssp.string.offset",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_blob_len,
+ { "Length", "ntlmssp.blob.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_blob_maxlen,
+ { "Maxlen", "ntlmssp.blob.maxlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_blob_offset,
+ { "Offset", "ntlmssp.blob.offset",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_version,
+ { "Version", "ntlmssp.version",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_version_major,
+ { "Major Version", "ntlmssp.version.major",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_version_minor,
+ { "Minor Version", "ntlmssp.version.minor",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_version_build_number,
+ { "Build Number", "ntlmssp.version.build_number",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_version_ntlm_current_revision,
+ { "NTLM Current Revision", "ntlmssp.version.ntlm_current_revision",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+
+/* Target Info */
+ { &hf_ntlmssp_challenge_target_info,
+ { "Target Info", "ntlmssp.challenge.target_info",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_challenge_target_info_len,
+ { "Length", "ntlmssp.challenge.target_info.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_challenge_target_info_maxlen,
+ { "Maxlen", "ntlmssp.challenge.target_info.maxlen",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ { &hf_ntlmssp_challenge_target_info_offset,
+ { "Offset", "ntlmssp.challenge.target_info.offset",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+
+ { &hf_ntlmssp_challenge_target_info_item_type,
+ { "Target Info Item Type", "ntlmssp.challenge.target_info.item.type",
+ FT_UINT16, BASE_HEX | BASE_EXT_STRING, &ntlm_name_types_ext, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_item_len,
+ { "Target Info Item Length", "ntlmssp.challenge.target_info.item.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+
+ { &hf_ntlmssp_challenge_target_info_end,
+ { "List End", "ntlmssp.challenge.target_info.end",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_nb_computer_name,
+ { "NetBIOS Computer Name", "ntlmssp.challenge.target_info.nb_computer_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Server NetBIOS Computer Name", HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_nb_domain_name,
+ { "NetBIOS Domain Name", "ntlmssp.challenge.target_info.nb_domain_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Server NetBIOS Domain Name", HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_dns_computer_name,
+ { "DNS Computer Name", "ntlmssp.challenge.target_info.dns_computer_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_dns_domain_name,
+ { "DNS Domain Name", "ntlmssp.challenge.target_info.dns_domain_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_dns_tree_name,
+ { "DNS Tree Name", "ntlmssp.challenge.target_info.dns_tree_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_flags,
+ { "Flags", "ntlmssp.challenge.target_info.flags",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_timestamp,
+ { "Timestamp", "ntlmssp.challenge.target_info.timestamp",
+ FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_restrictions,
+ { "Restrictions", "ntlmssp.challenge.target_info.restrictions",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_target_name,
+ { "Target Name", "ntlmssp.challenge.target_info.target_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_challenge_target_info_channel_bindings,
+ { "Channel Bindings", "ntlmssp.challenge.target_info.channel_bindings",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+
+ { &hf_ntlmssp_ntlmv2_response_item_type,
+ { "NTLMV2 Response Item Type", "ntlmssp.ntlmv2_response.item.type",
+ FT_UINT16, BASE_HEX | BASE_EXT_STRING, &ntlm_name_types_ext, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_item_len,
+ { "NTLMV2 Response Item Length", "ntlmssp.ntlmv2_response.item.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+
+ { &hf_ntlmssp_ntlmv2_response_end,
+ { "List End", "ntlmssp.ntlmv2_response.end",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_nb_computer_name,
+ { "NetBIOS Computer Name", "ntlmssp.ntlmv2_response.nb_computer_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Server NetBIOS Computer Name", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_nb_domain_name,
+ { "NetBIOS Domain Name", "ntlmssp.ntlmv2_response.nb_domain_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Server NetBIOS Domain Name", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_dns_computer_name,
+ { "DNS Computer Name", "ntlmssp.ntlmv2_response.dns_computer_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_dns_domain_name,
+ { "DNS Domain Name", "ntlmssp.ntlmv2_response.dns_domain_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_dns_tree_name,
+ { "DNS Tree Name", "ntlmssp.ntlmv2_response.dns_tree_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_flags,
+ { "Flags", "ntlmssp.ntlmv2_response.flags",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_timestamp,
+ { "Timestamp", "ntlmssp.ntlmv2_response.timestamp",
+ FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_restrictions,
+ { "Restrictions", "ntlmssp.ntlmv2_response.restrictions",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_target_name,
+ { "Target Name", "ntlmssp.ntlmv2_response.target_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_channel_bindings,
+ { "Channel Bindings", "ntlmssp.ntlmv2_response.channel_bindings",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+
+ { &hf_ntlmssp_message_integrity_code,
+ { "MIC", "ntlmssp.authenticate.mic",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "Message Integrity Code", HFILL}
+ },
+ { &hf_ntlmssp_verf,
+ { "NTLMSSP Verifier", "ntlmssp.verf",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_verf_vers,
+ { "Version Number", "ntlmssp.verf.vers",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_verf_body,
+ { "Verifier Body", "ntlmssp.verf.body",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+#if 0
+ { &hf_ntlmssp_decrypted_payload,
+ { "NTLM Decrypted Payload", "ntlmssp.decrypted_payload",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+#endif
+ { &hf_ntlmssp_verf_randompad,
+ { "Random Pad", "ntlmssp.verf.randompad",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_verf_crc32,
+ { "Verifier CRC32", "ntlmssp.verf.crc32",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_verf_hmacmd5,
+ { "HMAC MD5", "ntlmssp.verf.hmacmd5",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_verf_sequence,
+ { "Sequence", "ntlmssp.verf.sequence",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+
+ { &hf_ntlmssp_ntlmv2_response,
+ { "NTLMv2 Response", "ntlmssp.ntlmv2_response",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_ntproofstr,
+ { "NTProofStr", "ntlmssp.ntlmv2_response.ntproofstr",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "The HMAC-MD5 of the challenge", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_rversion,
+ { "Response Version", "ntlmssp.ntlmv2_response.rversion",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "The 1-byte response version, currently set to 1", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_hirversion,
+ { "Hi Response Version", "ntlmssp.ntlmv2_response.hirversion",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "The 1-byte highest response version understood by the client, currently set to 1", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_z,
+ { "Z", "ntlmssp.ntlmv2_response.z",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "byte array of zero bytes", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_pad,
+ { "padding", "ntlmssp.ntlmv2_response.pad",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_time,
+ { "Time", "ntlmssp.ntlmv2_response.time",
+ FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0,
+ "The 8-byte little-endian time in UTC", HFILL }
+ },
+ { &hf_ntlmssp_ntlmv2_response_chal,
+ { "NTLMv2 Client Challenge", "ntlmssp.ntlmv2_response.chal",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "The 8-byte NTLMv2 challenge message generated by the client", HFILL }
+ },
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL,
+ { "NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL",
+ FT_NONE, BASE_NONE, NULL, 0,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_Version,
+ { "Version", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.Version",
+ FT_UINT32, BASE_HEX, VALS(MSV1_0_CRED_VERSION), 0,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_Flags,
+ { "Flags", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.Flags",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_LM_PRESENT,
+ { "lm_present", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.LM_PRESENT",
+ FT_BOOLEAN, 32, NULL, MSV1_0_CRED_LM_PRESENT,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_NT_PRESENT,
+ { "nt_present", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.NT_PRESENT",
+ FT_BOOLEAN, 32, NULL, MSV1_0_CRED_NT_PRESENT,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_REMOVED,
+ { "removed", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.REMOVED",
+ FT_BOOLEAN, 32, NULL, MSV1_0_CRED_REMOVED,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_CREDKEY_PRESENT,
+ { "credkey_present", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.CREDKEY_PRESENT",
+ FT_BOOLEAN, 32, NULL, MSV1_0_CRED_CREDKEY_PRESENT,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_FLAG_SHA_PRESENT,
+ { "sha_present", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.SHA_PRESENT",
+ FT_BOOLEAN, 32, NULL, MSV1_0_CRED_SHA_PRESENT,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_CredentialKey,
+ { "CredentialKey", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.CredentialKey",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_CredentialKeyType,
+ { "CredentialKeyType", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.CredentialKeyType",
+ FT_UINT32, BASE_DEC, VALS(MSV1_0_CREDENTIAL_KEY_TYPE), 0,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_EncryptedCredsSize,
+ { "EncryptedCredsSize", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.EncryptedCredsSize",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ NULL, HFILL }},
+ { &hf_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL_EncryptedCreds,
+ { "EncryptedCreds", "ntlmssp.NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL.EncryptedCreds",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL }},
+ };
+
+
+ static gint *ett[] = {
+ &ett_ntlmssp,
+ &ett_ntlmssp_negotiate_flags,
+ &ett_ntlmssp_string,
+ &ett_ntlmssp_blob,
+ &ett_ntlmssp_version,
+ &ett_ntlmssp_challenge_target_info,
+ &ett_ntlmssp_challenge_target_info_item,
+ &ett_ntlmssp_ntlmv2_response,
+ &ett_ntlmssp_ntlmv2_response_item,
+ &ett_ntlmssp_NTLM_REMOTE_SUPPLEMENTAL_CREDENTIAL,
+ };
+ static ei_register_info ei[] = {
+ { &ei_ntlmssp_v2_key_too_long, { "ntlmssp.v2_key_too_long", PI_UNDECODED, PI_WARN, "NTLM v2 key is too long", EXPFILL }},
+ { &ei_ntlmssp_blob_len_too_long, { "ntlmssp.blob.length.too_long", PI_UNDECODED, PI_WARN, "Session blob length too long", EXPFILL }},
+ { &ei_ntlmssp_target_info_attr, { "ntlmssp.target_info_attr.unknown", PI_UNDECODED, PI_WARN, "Unknown NTLMSSP Target Info Attribute", EXPFILL }},
+ { &ei_ntlmssp_target_info_invalid, { "ntlmssp.target_info_attr.invalid", PI_UNDECODED, PI_WARN, "Invalid NTLMSSP Target Info AvPairs", EXPFILL }},
+ { &ei_ntlmssp_message_type, { "ntlmssp.messagetype.unknown", PI_PROTOCOL, PI_WARN, "Unrecognized NTLMSSP Message", EXPFILL }},
+ { &ei_ntlmssp_auth_nthash, { "ntlmssp.authenticated", PI_SECURITY, PI_CHAT, "Authenticated NTHASH", EXPFILL }},
+ { &ei_ntlmssp_sessionbasekey, { "ntlmssp.sessionbasekey", PI_SECURITY, PI_CHAT, "SessionBaseKey", EXPFILL }},
+ { &ei_ntlmssp_sessionkey, { "ntlmssp.sessionkey", PI_SECURITY, PI_CHAT, "SessionKey", EXPFILL }},
+ };
+ module_t *ntlmssp_module;
+ expert_module_t* expert_ntlmssp;
+
+ proto_ntlmssp = proto_register_protocol (
+ "NTLM Secure Service Provider", /* name */
+ "NTLMSSP", /* short name */
+ "ntlmssp" /* abbrev */
+ );
+ proto_register_field_array (proto_ntlmssp, hf, array_length (hf));
+ proto_register_subtree_array (ett, array_length (ett));
+ expert_ntlmssp = expert_register_protocol(proto_ntlmssp);
+ expert_register_field_array(expert_ntlmssp, ei, array_length(ei));
+ register_init_routine(&ntlmssp_init_protocol);
+ register_cleanup_routine(&ntlmssp_cleanup_protocol);
+
+ ntlmssp_module = prefs_register_protocol(proto_ntlmssp, NULL);
+
+ prefs_register_string_preference(ntlmssp_module, "nt_password",
+ "NT Password",
+ "Cleartext NT Password (used to decrypt payloads, supports only ASCII passwords)",
+ &ntlmssp_option_nt_password);
+
+ ntlmssp_handle = register_dissector("ntlmssp", dissect_ntlmssp, proto_ntlmssp);
+ ntlmssp_wrap_handle = register_dissector("ntlmssp_payload", dissect_ntlmssp_payload, proto_ntlmssp);
+ register_dissector("ntlmssp_data_only", dissect_ntlmssp_payload_only, proto_ntlmssp);
+ register_dissector("ntlmssp_verf", dissect_ntlmssp_verf, proto_ntlmssp);
+}
+
+void
+proto_reg_handoff_ntlmssp(void)
+{
+ /* Register protocol with the GSS-API module */
+
+ gssapi_init_oid("1.3.6.1.4.1.311.2.2.10", proto_ntlmssp, ett_ntlmssp,
+ ntlmssp_handle, ntlmssp_wrap_handle,
+ "NTLMSSP - Microsoft NTLM Security Support Provider");
+
+ /* Register authenticated pipe dissector */
+
+ /*
+ * XXX - the verifiers here seem to have a version of 1 and a body of all
+ * zeroes.
+ *
+ * XXX - DCE_C_AUTHN_LEVEL_CONNECT is, according to the DCE RPC 1.1
+ * spec, upgraded to DCE_C_AUTHN_LEVEL_PKT. Should we register
+ * any other levels here?
+ */
+ register_dcerpc_auth_subdissector(DCE_C_AUTHN_LEVEL_CONNECT,
+ DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP,
+ &ntlmssp_sign_fns);
+
+ register_dcerpc_auth_subdissector(DCE_C_AUTHN_LEVEL_PKT,
+ DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP,
+ &ntlmssp_sign_fns);
+
+ register_dcerpc_auth_subdissector(DCE_C_AUTHN_LEVEL_PKT_INTEGRITY,
+ DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP,
+ &ntlmssp_sign_fns);
+
+ register_dcerpc_auth_subdissector(DCE_C_AUTHN_LEVEL_PKT_PRIVACY,
+ DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP,
+ &ntlmssp_seal_fns);
+ ntlmssp_tap = register_tap("ntlmssp");
+
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */