aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rw-r--r--Makefile.am4
-rw-r--r--Makefile.nmake3
-rw-r--r--crypt-des.c311
-rw-r--r--crypt-des.h26
-rw-r--r--packet-dcerpc.c237
-rw-r--r--packet-ntlmssp.c536
7 files changed, 982 insertions, 137 deletions
diff --git a/AUTHORS b/AUTHORS
index 3c5b63e638..e58079c39c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1234,6 +1234,8 @@ Devin Heitmueller <dheitmueller[AT]netilla.com> {
UnicodeChangePassword2
Supply offset to dissectors for connection-oriented DCERPC PDU
types
+ Support for decrypting DCERPC conversations using NTLMSSP
+ version 1
}
Chenjiang Hu <chu[AT]chiaro.com> {
diff --git a/Makefile.am b/Makefile.am
index f6a9b76a34..d56ac0fcb4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
# Makefile.am
# Automake file for Ethereal
#
-# $Id: Makefile.am,v 1.530 2002/12/20 07:56:07 sharpe Exp $
+# $Id: Makefile.am,v 1.531 2002/12/31 08:05:28 guy Exp $
#
# Ethereal - Network traffic analyzer
# By Gerald Combs <gerald@ethereal.com>
@@ -626,6 +626,8 @@ ETHEREAL_COMMON_SRC = \
column.h \
conditions.c \
conditions.h \
+ crypt-des.c \
+ crypt-des.h \
crypt-md4.c \
crypt-md4.h \
crypt-md5.c \
diff --git a/Makefile.nmake b/Makefile.nmake
index fb41bd30b8..19b8f44e44 100644
--- a/Makefile.nmake
+++ b/Makefile.nmake
@@ -1,7 +1,7 @@
## Makefile for building ethereal.exe with Microsoft C and nmake
## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake
#
-# $Id: Makefile.nmake,v 1.264 2002/12/18 17:44:54 gerald Exp $
+# $Id: Makefile.nmake,v 1.265 2002/12/31 08:05:29 guy Exp $
include config.nmake
include <win32.mak>
@@ -351,6 +351,7 @@ ETHEREAL_COMMON_OBJECTS = \
cfile.obj \
column.obj \
conditions.obj \
+ crypt-des.obj \
crypt-md4.obj \
crypt-md5.obj \
crypt-rc4.obj \
diff --git a/crypt-des.c b/crypt-des.c
new file mode 100644
index 0000000000..f39e821579
--- /dev/null
+++ b/crypt-des.c
@@ -0,0 +1,311 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a partial implementation of DES designed for use in the
+ SMB authentication protocol
+
+ Copyright (C) Andrew Tridgell 1998
+
+ $Id: crypt-des.c,v 1.1 2002/12/31 08:05:29 guy Exp $
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <glib.h>
+
+/* NOTES:
+
+ This code makes no attempt to be fast! In fact, it is a very
+ slow implementation
+
+ This code is NOT a complete DES implementation. It implements only
+ the minimum necessary for SMB authentication, as used by all SMB
+ products (including every copy of Microsoft Windows95 ever sold)
+
+ In particular, it can only do a unchained forward DES pass. This
+ means it is not possible to use this code for encryption/decryption
+ of data, instead it is only useful as a "hash" algorithm.
+
+ There is no entry point into this code that allows normal DES operation.
+
+ I believe this means that this code does not come under ITAR
+ regulations but this is NOT a legal opinion. If you are concerned
+ about the applicability of ITAR regulations to this code then you
+ should confirm it for yourself (and maybe let me know if you come
+ up with a different answer to the one above)
+*/
+
+
+#define uchar unsigned char
+
+static const uchar perm1[56] = {57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4};
+
+static const uchar perm2[48] = {14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32};
+
+static const uchar perm3[64] = {58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7};
+
+static const uchar perm4[48] = { 32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1};
+
+static const uchar perm5[32] = { 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25};
+
+
+static const uchar perm6[64] ={ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25};
+
+
+static const uchar sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static const uchar sbox[8][4][16] = {
+ {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
+
+ {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
+
+ {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
+
+ {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
+
+ {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
+
+ {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
+
+ {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
+
+ {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
+
+static void permute(char *out, const char *in, const uchar *p, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in[p[i]-1];
+}
+
+static void lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = d[(i+count)%n];
+ for (i=0;i<n;i++)
+ d[i] = out[i];
+}
+
+static void concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void dohash(char *out, char *in, char *key, int forw)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i=0;i<28;i++)
+ c[i] = pk1[i];
+ for (i=0;i<28;i++)
+ d[i] = pk1[i+28];
+
+ for (i=0;i<16;i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j=0;j<32;j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j+32];
+ }
+
+ for (i=0;i<16;i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[forw ? i : 15 - i], 48);
+
+ for (j=0;j<8;j++)
+ for (k=0;k<6;k++)
+ b[j][k] = erk[j*6 + k];
+
+ for (j=0;j<8;j++) {
+ int m, n;
+ m = (b[j][0]<<1) | b[j][5];
+
+ n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4];
+
+ for (k=0;k<4;k++)
+ b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0;
+ }
+
+ for (j=0;j<8;j++)
+ for (k=0;k<4;k++)
+ cb[j*4+k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j=0;j<32;j++)
+ l[j] = r[j];
+
+ for (j=0;j<32;j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+static void str_to_key(const unsigned char *str,unsigned char *key)
+{
+ int i;
+
+ key[0] = str[0]>>1;
+ key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
+ key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
+ key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
+ key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
+ key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
+ key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
+ key[7] = str[6]&0x7F;
+ for (i=0;i<8;i++) {
+ key[i] = (key[i]<<1);
+ }
+}
+
+
+void crypt_des_ecb(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ unsigned char key2[8];
+
+ str_to_key(key, key2);
+
+ for (i=0;i<64;i++) {
+ inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ outb[i] = 0;
+ }
+
+ dohash(outb, inb, keyb, forw);
+
+ for (i=0;i<8;i++) {
+ out[i] = 0;
+ }
+
+ for (i=0;i<64;i++) {
+ if (outb[i])
+ out[i/8] |= (1<<(7-(i%8)));
+ }
+}
+
diff --git a/crypt-des.h b/crypt-des.h
new file mode 100644
index 0000000000..8f799be2d6
--- /dev/null
+++ b/crypt-des.h
@@ -0,0 +1,26 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a partial implementation of DES designed for use in the
+ SMB authentication protocol
+
+ Copyright (C) Andrew Tridgell 1998
+
+ $Id: crypt-des.h,v 1.1 2002/12/31 08:05:29 guy Exp $
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+void crypt_des_ecb(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw);
diff --git a/packet-dcerpc.c b/packet-dcerpc.c
index ac35fcbd44..4e6b89ce25 100644
--- a/packet-dcerpc.c
+++ b/packet-dcerpc.c
@@ -2,7 +2,7 @@
* Routines for DCERPC packet disassembly
* Copyright 2001, Todd Sabin <tas@webspan.net>
*
- * $Id: packet-dcerpc.c,v 1.92 2002/12/19 11:22:16 sahlberg Exp $
+ * $Id: packet-dcerpc.c,v 1.93 2002/12/31 08:05:29 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -404,7 +404,8 @@ static gint ett_dcerpc_fragments = -1;
static gint ett_dcerpc_fragment = -1;
static gint ett_decrpc_krb5_auth_verf = -1;
-static dissector_handle_t ntlmssp_handle, ntlmssp_verf_handle;
+static dissector_handle_t ntlmssp_handle, ntlmssp_verf_handle,
+ ntlmssp_enc_payload_handle;
static dissector_handle_t gssapi_handle, gssapi_verf_handle;
static const fragment_items dcerpc_frag_items = {
@@ -422,6 +423,13 @@ static const fragment_items dcerpc_frag_items = {
"fragments"
};
+typedef struct _dcerpc_auth_info {
+ guint8 auth_pad_len;
+ guint8 auth_level;
+ guint8 auth_type;
+ guint32 auth_size;
+} dcerpc_auth_info;
+
/* try to desegment big DCE/RPC packets over TCP? */
static gboolean dcerpc_cn_desegment = TRUE;
@@ -1315,7 +1323,7 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
proto_tree *dcerpc_tree,
tvbuff_t *tvb, gint offset,
char *drep, dcerpc_info *info,
- int auth_level)
+ dcerpc_auth_info *auth_info)
{
dcerpc_uuid_key key;
dcerpc_uuid_value *sub_proto;
@@ -1393,14 +1401,28 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
/*
* If the authentication level is DCE_C_AUTHN_LEVEL_PKT_PRIVACY,
- * the stub data is encrypted, and we can't dissect it.
+ * the stub data is encrypted, and we'd have to decrypt it in
+ * order to dissect it.
*/
- if (auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) {
+ if (auth_info != NULL &&
+ auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) {
length = tvb_length_remaining (tvb, offset);
if (length > 0) {
proto_tree_add_text(sub_tree, tvb, offset, length,
"Encrypted stub data (%d byte%s)",
length, plurality(length, "", "s"));
+
+ switch (auth_info->auth_type) {
+
+ case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
+ /* NTLMSSP */
+ tvbuff_t *ntlmssp_tvb;
+ ntlmssp_tvb = tvb_new_subset(tvb, offset, length, length);
+
+ call_dissector(ntlmssp_enc_payload_handle, ntlmssp_tvb, pinfo,
+ sub_tree);
+ break;
+ }
}
} else {
sub_dissect = info->request ? proc->dissect_rqst : proc->dissect_resp;
@@ -1429,21 +1451,62 @@ dcerpc_try_handoff (packet_info *pinfo, proto_tree *tree,
}
static int
+dissect_dcerpc_verifier (tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr,
+ dcerpc_auth_info *auth_info)
+{
+ int auth_offset;
+
+ if (auth_info->auth_size != 0) {
+ auth_offset = hdr->frag_len - hdr->auth_len;
+ switch (auth_info->auth_type) {
+
+ case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
+ /* NTLMSSP */
+ tvbuff_t *ntlmssp_tvb;
+
+ ntlmssp_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len,
+ hdr->auth_len);
+
+ call_dissector(ntlmssp_verf_handle, ntlmssp_tvb, pinfo,
+ dcerpc_tree);
+
+ break;
+ }
+
+ case DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO: {
+ /* SPNEGO (rfc2478) */
+ tvbuff_t *gssapi_tvb;
+
+ gssapi_tvb = tvb_new_subset(tvb, auth_offset, hdr->auth_len,
+ hdr->auth_len);
+
+ call_dissector(gssapi_verf_handle, gssapi_tvb, pinfo, dcerpc_tree);
+
+ break;
+ }
+
+ default:
+ proto_tree_add_text (dcerpc_tree, tvb, auth_offset, hdr->auth_len,
+ "Auth Verifier");
+ }
+ }
+
+ return hdr->auth_len;
+}
+
+static void
dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tree,
- e_dce_cn_common_hdr_t *hdr, int *auth_level_p,
- gboolean are_credentials)
+ e_dce_cn_common_hdr_t *hdr, gboolean are_credentials,
+ dcerpc_auth_info *auth_info)
{
int offset;
- guint8 auth_pad_len;
- guint8 auth_level;
- guint8 auth_type;
/*
- * Initially set "*auth_level_p" to -1 to indicate that we haven't
+ * Initially set auth_level to -1 to indicate that we haven't
* yet seen any authentication level information.
*/
- if (auth_level_p != NULL)
- *auth_level_p = -1;
+ auth_info->auth_level = -1;
/*
* The authentication information is at the *end* of the PDU; in
@@ -1460,13 +1523,15 @@ dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
offset = hdr->frag_len - (hdr->auth_len + 8);
offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
- hf_dcerpc_auth_type, &auth_type);
+ hf_dcerpc_auth_type,
+ &auth_info->auth_type);
offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
- hf_dcerpc_auth_level, &auth_level);
- if (auth_level_p != NULL)
- *auth_level_p = auth_level;
+ hf_dcerpc_auth_level,
+ &auth_info->auth_level);
+
offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
- hf_dcerpc_auth_pad_len, &auth_pad_len);
+ hf_dcerpc_auth_pad_len,
+ &auth_info->auth_pad_len);
offset = dissect_dcerpc_uint8 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
hf_dcerpc_auth_rsrvd, NULL);
offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
@@ -1479,7 +1544,7 @@ dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
/*
* The authentication data are credentials.
*/
- switch (auth_type) {
+ switch (auth_info->auth_type) {
case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
/* NTLMSSP */
@@ -1510,54 +1575,30 @@ dissect_dcerpc_cn_auth (tvbuff_t *tvb, packet_info *pinfo, proto_tree *dcerpc_tr
proto_tree_add_text (dcerpc_tree, tvb, offset, hdr->auth_len,
"Auth Credentials");
}
- } else {
- /*
- * The authentication data are a verifier.
- */
- switch (auth_type) {
-
- case DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP: {
- /* NTLMSSP */
- tvbuff_t *ntlmssp_tvb;
-
- ntlmssp_tvb = tvb_new_subset(tvb, offset, hdr->auth_len,
- hdr->auth_len);
-
- call_dissector(ntlmssp_verf_handle, ntlmssp_tvb, pinfo,
- dcerpc_tree);
-
- break;
- }
-
- case DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO: {
- /* SPNEGO (rfc2478) */
- tvbuff_t *gssapi_tvb;
-
- gssapi_tvb = tvb_new_subset(tvb, offset, hdr->auth_len,
- hdr->auth_len);
-
- call_dissector(gssapi_verf_handle, gssapi_tvb, pinfo, dcerpc_tree);
-
- break;
- }
-
- default:
- proto_tree_add_text (dcerpc_tree, tvb, offset, hdr->auth_len,
- "Auth Verifier");
- }
}
+ /*
+ * XXX - sometimes the padding is a multiple of 4 and greater
+ * than 3, meaning it's not padding to put the authentication
+ * data on a 4-byte boundary.
+ *
+ * For now, we take its value mod 4.
+ *
+ * XXX - what is going on there?
+ */
+ auth_info->auth_pad_len %= 4;
+
/* figure out where the auth padding starts */
- offset = hdr->frag_len - (hdr->auth_len + 8 + auth_pad_len);
- if (offset > 0 && auth_pad_len) {
+ offset = hdr->frag_len - (hdr->auth_len + 8 + auth_info->auth_pad_len);
+ if (offset > 0 && auth_info->auth_pad_len) {
proto_tree_add_text (dcerpc_tree, tvb, offset,
- auth_pad_len, "Auth padding");
- return hdr->auth_len + 8 + auth_pad_len;
+ auth_info->auth_pad_len, "Auth padding");
+ auth_info->auth_size = hdr->auth_len + 8 + auth_info->auth_pad_len;
} else {
- return hdr->auth_len + 8;
+ auth_info->auth_size = hdr->auth_len + 8;
}
} else {
- return 0;
+ auth_info->auth_size = 0;
}
}
@@ -1604,6 +1645,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
guint16 if_ver, if_ver_minor;
char uuid_str[DCERPC_UUID_STR_LEN];
int uuid_str_len;
+ dcerpc_auth_info auth_info;
offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
hf_dcerpc_cn_max_xmit, NULL);
@@ -1740,7 +1782,7 @@ dissect_dcerpc_cn_bind (tvbuff_t *tvb, gint offset, packet_info *pinfo,
* an authentication header, and associate it with an authentication
* context, so subsequent PDUs can use that context.
*/
- dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, NULL, TRUE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, TRUE, &auth_info);
}
static void
@@ -1757,6 +1799,7 @@ dissect_dcerpc_cn_bind_ack (tvbuff_t *tvb, gint offset, packet_info *pinfo,
guint32 trans_ver;
char uuid_str[DCERPC_UUID_STR_LEN];
int uuid_str_len;
+ dcerpc_auth_info auth_info;
offset = dissect_dcerpc_uint16 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
hf_dcerpc_cn_max_xmit, &max_xmit);
@@ -1826,7 +1869,7 @@ dissect_dcerpc_cn_bind_ack (tvbuff_t *tvb, gint offset, packet_info *pinfo,
* XXX - do we need to do anything with the authentication level
* we get back from this?
*/
- dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, NULL, TRUE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, TRUE, &auth_info);
if (check_col (pinfo->cinfo, COL_INFO)) {
if (num_results != 0 && result == 0) {
@@ -1882,7 +1925,7 @@ static void
dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *dcerpc_tree, proto_tree *tree,
e_dce_cn_common_hdr_t *hdr, dcerpc_info *di,
- int auth_sz, int auth_level, guint32 alloc_hint,
+ dcerpc_auth_info *auth_info, guint32 alloc_hint,
guint32 frame)
{
int length, reported_length, stub_length;
@@ -1890,7 +1933,7 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
length = tvb_length_remaining(tvb, offset);
reported_length = tvb_reported_length_remaining(tvb, offset);
- stub_length = hdr->frag_len - offset - auth_sz;
+ stub_length = hdr->frag_len - offset - auth_info->auth_size;
if (length > stub_length)
length = stub_length;
if (reported_length > stub_length)
@@ -1911,7 +1954,7 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
dcerpc_try_handoff (pinfo, tree, dcerpc_tree,
tvb_new_subset (tvb, offset, length,
reported_length),
- 0, hdr->drep, di, auth_level);
+ 0, hdr->drep, di, auth_info);
} else {
/* PDU is fragmented and this isn't the first fragment */
if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -1978,7 +2021,7 @@ dissect_dcerpc_cn_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
pinfo->fragmented = FALSE;
dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb,
- 0, hdr->drep, di, auth_level);
+ 0, hdr->drep, di, auth_info);
} else {
/* Reassembly not complete - some fragments
are missing */
@@ -2016,8 +2059,7 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
guint16 ctx_id;
guint16 opnum;
e_uuid_t obj_id;
- int auth_sz = 0;
- int auth_level;
+ dcerpc_auth_info auth_info;
guint32 alloc_hint;
int length;
char uuid_str[DCERPC_UUID_STR_LEN];
@@ -2064,13 +2106,17 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
* XXX - what if this was set when the connection was set up,
* and we just have a security context?
*/
- auth_sz = dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr,
- &auth_level, FALSE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype,
pinfo->srcport, pinfo->destport, 0);
if (!conv) {
-
+ length = tvb_length_remaining (tvb, offset);
+ if (length > 0) {
+ proto_tree_add_text (dcerpc_tree, tvb, offset, length,
+ "Stub data (%d byte%s)", length,
+ plurality(length, "", "s"));
+ }
} else {
dcerpc_call_value *value;
@@ -2139,7 +2185,7 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
}
dissect_dcerpc_cn_stub (tvb, offset, pinfo, dcerpc_tree, tree,
- hdr, &di, auth_sz, auth_level, alloc_hint,
+ hdr, &di, &auth_info, alloc_hint,
value->req_frame);
} else {
length = tvb_length_remaining (tvb, offset);
@@ -2150,6 +2196,9 @@ dissect_dcerpc_cn_rqst (tvbuff_t *tvb, gint offset, packet_info *pinfo,
}
}
}
+
+ /* Decrypt the verifier, if present */
+ dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info);
}
static void
@@ -2159,8 +2208,7 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
dcerpc_call_value *value = NULL;
conversation_t *conv;
guint16 ctx_id;
- int auth_sz = 0;
- int auth_level;
+ dcerpc_auth_info auth_info;
guint32 alloc_hint;
int length;
@@ -2183,13 +2231,18 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
* XXX - what if this was set when the connection was set up,
* and we just have a security context?
*/
- auth_sz = dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr,
- &auth_level, FALSE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype,
pinfo->srcport, pinfo->destport, 0);
if (!conv) {
/* no point in creating one here, really */
+ length = tvb_length_remaining (tvb, offset);
+ if (length > 0) {
+ proto_tree_add_text (dcerpc_tree, tvb, offset, length,
+ "Stub data (%d byte%s)", length,
+ plurality(length, "", "s"));
+ }
} else {
/* !!! we can NOT check flags.visited here since this will interact
@@ -2241,7 +2294,7 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
}
dissect_dcerpc_cn_stub (tvb, offset, pinfo, dcerpc_tree, tree,
- hdr, &di, auth_sz, auth_level, alloc_hint,
+ hdr, &di, &auth_info, alloc_hint,
value->rep_frame);
} else {
length = tvb_length_remaining (tvb, offset);
@@ -2252,6 +2305,9 @@ dissect_dcerpc_cn_resp (tvbuff_t *tvb, gint offset, packet_info *pinfo,
}
}
}
+
+ /* Decrypt the verifier, if present */
+ dissect_dcerpc_verifier (tvb, pinfo, dcerpc_tree, hdr, &auth_info);
}
static void
@@ -2262,9 +2318,8 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
conversation_t *conv;
guint16 ctx_id;
guint32 status;
- int auth_sz = 0;
- int auth_level;
guint32 alloc_hint;
+ dcerpc_auth_info auth_info;
offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, hdr->drep,
hf_dcerpc_cn_alloc_hint, &alloc_hint);
@@ -2294,8 +2349,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
* XXX - what if this was set when the connection was set up,
* and we just have a security context?
*/
- auth_sz = dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr,
- &auth_level, FALSE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, hdr, FALSE, &auth_info);
conv = find_conversation (&pinfo->src, &pinfo->dst, pinfo->ptype,
pinfo->srcport, pinfo->destport, 0);
@@ -2354,7 +2408,7 @@ dissect_dcerpc_cn_fault (tvbuff_t *tvb, gint offset, packet_info *pinfo,
length = tvb_length_remaining(tvb, offset);
reported_length = tvb_reported_length_remaining(tvb, offset);
- stub_length = hdr->frag_len - offset - auth_sz;
+ stub_length = hdr->frag_len - offset - auth_info.auth_size;
if (length > stub_length)
length = stub_length;
if (reported_length > stub_length)
@@ -2513,6 +2567,7 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *cn_flags_tree = NULL;
proto_tree *drep_tree = NULL;
e_dce_cn_common_hdr_t hdr;
+ dcerpc_auth_info auth_info;
/*
* when done over nbt, dcerpc requests are padded with 4 bytes of null
@@ -2634,7 +2689,8 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
/*
* Nothing after the common header other than credentials.
*/
- dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, NULL, TRUE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, TRUE,
+ &auth_info);
break;
case PDU_REQ:
@@ -2659,7 +2715,8 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
* Nothing after the common header other than an authentication
* verifier.
*/
- dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, NULL, FALSE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, FALSE,
+ &auth_info);
break;
case PDU_SHUTDOWN:
@@ -2671,7 +2728,8 @@ dissect_dcerpc_cn (tvbuff_t *tvb, int offset, packet_info *pinfo,
default:
/* might as well dissect the auth info */
- dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, NULL, FALSE);
+ dissect_dcerpc_cn_auth (tvb, pinfo, dcerpc_tree, &hdr, FALSE,
+ &auth_info);
break;
}
return hdr.frag_len + padding;
@@ -2951,13 +3009,13 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
/* First fragment, possibly the only fragment */
/*
- * XXX - authentication level?
+ * XXX - authentication info?
*/
pinfo->fragmented = (hdr->flags1 & PFCL1_FRAG);
dcerpc_try_handoff (pinfo, tree, dcerpc_tree,
tvb_new_subset (tvb, offset, length,
reported_length),
- 0, hdr->drep, di, 0);
+ 0, hdr->drep, di, NULL);
} else {
/* PDU is fragmented and this isn't the first fragment */
if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -2998,11 +3056,11 @@ dissect_dcerpc_dg_stub (tvbuff_t *tvb, int offset, packet_info *pinfo,
dcerpc_tree, pinfo, next_tvb);
/*
- * XXX - authentication level?
+ * XXX - authentication info?
*/
pinfo->fragmented = FALSE;
dcerpc_try_handoff (pinfo, tree, dcerpc_tree, next_tvb,
- 0, hdr->drep, di, 0);
+ 0, hdr->drep, di, NULL);
} else {
/* Reassembly isn't completed yet */
if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -3809,6 +3867,7 @@ proto_reg_handoff_dcerpc (void)
heur_dissector_add ("smb_transact", dissect_dcerpc_cn_bs, proto_dcerpc);
ntlmssp_handle = find_dissector("ntlmssp");
ntlmssp_verf_handle = find_dissector("ntlmssp_verf");
+ ntlmssp_enc_payload_handle = find_dissector("ntlmssp_encrypted_payload");
gssapi_handle = find_dissector("gssapi");
gssapi_verf_handle = find_dissector("gssapi_verf");
}
diff --git a/packet-ntlmssp.c b/packet-ntlmssp.c
index c04d7632f6..875122df4a 100644
--- a/packet-ntlmssp.c
+++ b/packet-ntlmssp.c
@@ -2,7 +2,7 @@
* Routines for NTLM Secure Service Provider
* Devin Heitmueller <dheitmueller@netilla.com>
*
- * $Id: packet-ntlmssp.c,v 1.32 2002/11/28 06:48:42 guy Exp $
+ * $Id: packet-ntlmssp.c,v 1.33 2002/12/31 08:05:29 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -34,6 +34,10 @@
#include "asn1.h" /* XXX - needed for subid_t */
#include "packet-gssapi.h"
#include "packet-frame.h"
+#include "prefs.h"
+#include "crypt-rc4.h"
+#include "crypt-md4.h"
+#include "crypt-des.h"
/* Message types */
@@ -156,8 +160,13 @@ static int hf_ntlmssp_address_list_server_nb = -1;
static int hf_ntlmssp_address_list_domain_nb = -1;
static int hf_ntlmssp_address_list_server_dns = -1;
static int hf_ntlmssp_address_list_domain_dns = -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_unknown1 = -1;
+static int hf_ntlmssp_verf_crc32 = -1;
+static int hf_ntlmssp_verf_sequence = -1;
+static int hf_ntlmssp_decrypted_payload = -1;
static gint ett_ntlmssp = -1;
static gint ett_ntlmssp_negotiate_flags = -1;
@@ -165,8 +174,128 @@ static gint ett_ntlmssp_string = -1;
static gint ett_ntlmssp_blob = -1;
static gint ett_ntlmssp_address_list = -1;
-static GMemChunk *flag_info_chunk;
-static int flag_info_chunk_count = 100;
+/* Configuration variables */
+static char *nt_password = NULL;
+
+#define MAX_BLOB_SIZE 256
+typedef struct _ntlmssp_blob {
+ guint16 length;
+ guint8 contents[MAX_BLOB_SIZE];
+} ntlmssp_blob;
+
+/* Used in the conversation function */
+typedef struct _ntlmssp_info {
+ guint32 flags;
+ guint8 challenge[8];
+ rc4_state_struct rc4_state_peer1;
+ rc4_state_struct rc4_state_peer2;
+ guint32 peer1_dest_port;
+ int rc4_state_initialized;
+ ntlmssp_blob ntlm_response;
+ ntlmssp_blob lm_response;
+} ntlmssp_info;
+
+/* If this struct exists in the payload_decrypt, then we have already
+ decrypted it once */
+typedef struct _ntlmssp_packet_info {
+ guint32 flags;
+ guint8 challenge[8];
+ guint8 decrypted_payload[1500]; /* 1500 is an arbitrary size */
+ guint8 verifier[16];
+ gboolean payload_decrypted;
+ gboolean verifier_decrypted;
+} ntlmssp_packet_info;
+
+/*
+ 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, 1);
+ crypt_des_ecb(response + 8, challenge, pw21 + 7, 1);
+ crypt_des_ecb(response + 16, challenge, pw21 + 14, 1);
+
+ return 1;
+}
+
+/* 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 16 octets)
+ */
+static void
+create_ntlmssp_v1_key(const char *nt_password, const guint8 *challenge,
+ int use_key_128, guint8 *sspkey)
+{
+ unsigned char lm_password_upper[16];
+ unsigned char lm_password_hash[16];
+ guint8 lm_challenge_response[24];
+ guint8 rc4key[24];
+ guint8 pw21[21]; /* Password hash padded to 21 bytes */
+ size_t password_len;
+ unsigned int i;
+ unsigned char lmhash_key[] =
+ {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+
+ memset(lm_password_upper, 0, sizeof(lm_password_upper));
+
+ /* Create a Lan Manager hash of the input password */
+ if (nt_password) {
+ password_len = strlen(nt_password);
+ /* Truncate password if too long */
+ if (password_len > 16)
+ password_len = 16;
+ for (i = 0; i < password_len; i++) {
+ lm_password_upper[i] = toupper(nt_password[i]);
+ }
+ }
+
+ crypt_des_ecb(lm_password_hash, lmhash_key, lm_password_upper, 1);
+ crypt_des_ecb(lm_password_hash+8, lmhash_key, lm_password_upper+7, 1);
+
+ /* Generate the LanMan Challenge Response */
+ ntlmssp_generate_challenge_response(lm_challenge_response,
+ lm_password_hash, challenge);
+
+ /* Generate the NTLMSSP-v1 RC4 Key.
+ * The RC4 key is derived from the Lan Manager Hash.
+ * See lkcl "DCE/RPC over SMB" page 254 for the algorithm.
+ */
+ memset(pw21, 0xBD, sizeof(pw21));
+ memcpy(pw21, lm_password_hash, sizeof(lm_password_hash));
+
+ /* Only the first eight bytes of challenge_reponse is used */
+ crypt_des_ecb(rc4key, lm_challenge_response, pw21, 1);
+ crypt_des_ecb(rc4key + 8, lm_challenge_response, pw21 + 7, 1);
+ crypt_des_ecb(rc4key + 16, lm_challenge_response, pw21 + 14, 1);
+
+ /* Create the SSP Key */
+ memset(sspkey, 0, sizeof(sspkey));
+ if (use_key_128) {
+ /* Create 128 bit key */
+ memcpy(sspkey, rc4key, 16);
+ }
+ else {
+ /* Create 40 bit key */
+ memcpy(sspkey, rc4key, 5);
+ sspkey[5]=0xe5;
+ sspkey[6]=0x38;
+ sspkey[7]=0xb0;
+ }
+ return;
+}
/* dissect a string - header area contains:
two byte len
@@ -234,7 +363,7 @@ dissect_ntlmssp_string (tvbuff_t *tvb, int offset,
static int
dissect_ntlmssp_blob (tvbuff_t *tvb, int offset,
proto_tree *ntlmssp_tree,
- int blob_hf, int *end)
+ int blob_hf, int *end, ntlmssp_blob *result)
{
proto_item *tf = NULL;
proto_tree *tree = NULL;
@@ -266,6 +395,15 @@ dissect_ntlmssp_blob (tvbuff_t *tvb, int offset,
offset += 4;
*end = blob_offset + blob_length;
+
+ if (result != NULL) {
+ result->length = blob_length;
+ memset(result->contents, 0, MAX_BLOB_SIZE);
+ if (blob_length < MAX_BLOB_SIZE)
+ tvb_memcpy(tvb, result->contents, blob_offset, blob_length);
+ }
+
+
return offset;
}
@@ -504,34 +642,17 @@ dissect_ntlmssp_challenge (tvbuff_t *tvb, packet_info *pinfo, int offset,
guint32 negotiate_flags;
int item_start, item_end;
int data_start, data_end;
- guint32 *flag_info;
+ ntlmssp_info *conv_ntlmssp_info;
conversation_t *conversation;
gboolean unicode_strings = FALSE;
+ guint8 sspkey[16]; /* NTLMSSP cipher key */
+ guint8 ssp_key_len; /* Either 8 or 16 (40 bit or 128) */
/* need to find unicode flag */
negotiate_flags = tvb_get_letohl (tvb, offset+8);
if (negotiate_flags & NTLMSSP_NEGOTIATE_UNICODE)
unicode_strings = TRUE;
- /*
- * Store the flags with the conversation, as they're needed in order
- * to dissect subsequent messages.
- */
- conversation = find_conversation(&pinfo->src, &pinfo->dst,
- pinfo->ptype, pinfo->srcport,
- pinfo->destport, 0);
- if (!conversation) { /* Create one */
- conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
- pinfo->srcport, pinfo->destport, 0);
- }
-
- if (!conversation_get_proto_data(conversation, proto_ntlmssp)) {
- flag_info = g_mem_chunk_alloc(flag_info_chunk);
- *flag_info = negotiate_flags;
-
- conversation_add_proto_data(conversation, proto_ntlmssp, flag_info);
- }
-
/* Domain name */
offset = dissect_ntlmssp_string(tvb, offset, ntlmssp_tree, unicode_strings,
hf_ntlmssp_challenge_domain,
@@ -547,6 +668,45 @@ dissect_ntlmssp_challenge (tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_tree_add_item (ntlmssp_tree,
hf_ntlmssp_ntlm_challenge,
tvb, offset, 8, FALSE);
+
+ /*
+ * Store the flags and the challenge with the conversation, as they're
+ * needed in order to dissect subsequent messages.
+ */
+ conversation = find_conversation(&pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ pinfo->destport, 0);
+ if (!conversation) { /* Create one */
+ conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ }
+
+ if (!conversation_get_proto_data(conversation, proto_ntlmssp)) {
+ conv_ntlmssp_info = g_malloc(sizeof(ntlmssp_info));
+ /* Insert the flags into the conversation */
+ conv_ntlmssp_info->flags = negotiate_flags;
+ /* Insert the challenge into the conversation */
+ tvb_memcpy(tvb, conv_ntlmssp_info->challenge, offset, 8);
+
+ /* Between the challenge and the user provided password, we can build the
+ NTLMSSP key and initialize the cipher */
+ if (conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_128) {
+ create_ntlmssp_v1_key(nt_password, conv_ntlmssp_info->challenge,
+ 1, sspkey);
+ ssp_key_len = 16;
+ }
+ else {
+ create_ntlmssp_v1_key(nt_password, conv_ntlmssp_info->challenge,
+ 0, sspkey);
+ ssp_key_len = 8;
+ }
+ crypt_rc4_init(&conv_ntlmssp_info->rc4_state_peer1, sspkey, ssp_key_len);
+ crypt_rc4_init(&conv_ntlmssp_info->rc4_state_peer2, sspkey, ssp_key_len);
+ conv_ntlmssp_info->peer1_dest_port = pinfo->destport;
+ conv_ntlmssp_info->rc4_state_initialized = 1;
+
+ conversation_add_proto_data(conversation, proto_ntlmssp, conv_ntlmssp_info);
+ }
offset += 8;
/* Reserved (function not completely known) */
@@ -581,7 +741,7 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset,
int data_start, data_end = 0;
guint32 negotiate_flags;
gboolean unicode_strings = FALSE;
- guint32 *flag_info;
+ ntlmssp_info *conv_ntlmssp_info;
conversation_t *conversation;
/*
@@ -590,8 +750,8 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset,
* the AUTHENTICATE message, so we can't figure out whether
* strings are Unicode or not by looking at *our* flags.
*/
- flag_info = p_get_proto_data(pinfo->fd, proto_ntlmssp);
- if (flag_info == NULL) {
+ conv_ntlmssp_info = p_get_proto_data(pinfo->fd, proto_ntlmssp);
+ 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
@@ -601,17 +761,17 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset,
pinfo->ptype, pinfo->srcport,
pinfo->destport, 0);
if (conversation != NULL) {
- flag_info = conversation_get_proto_data(conversation, proto_ntlmssp);
- if (flag_info != NULL) {
+ conv_ntlmssp_info = conversation_get_proto_data(conversation, proto_ntlmssp);
+ if (conv_ntlmssp_info != NULL) {
/*
* We have flag info; attach it to the frame.
*/
- p_add_proto_data(pinfo->fd, proto_ntlmssp, flag_info);
+ p_add_proto_data(pinfo->fd, proto_ntlmssp, conv_ntlmssp_info);
}
}
}
- if (flag_info != NULL) {
- if (*flag_info & NTLMSSP_NEGOTIATE_UNICODE)
+ if (conv_ntlmssp_info != NULL) {
+ if (conv_ntlmssp_info->flags & NTLMSSP_NEGOTIATE_UNICODE)
unicode_strings = TRUE;
}
@@ -635,14 +795,18 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset,
data_start = tvb_get_letohl(tvb, offset+4);
offset = dissect_ntlmssp_blob(tvb, offset, ntlmssp_tree,
hf_ntlmssp_auth_lmresponse,
- &item_end);
+ &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, offset, ntlmssp_tree,
hf_ntlmssp_auth_ntresponse,
- &item_end);
+ &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);
@@ -677,7 +841,7 @@ dissect_ntlmssp_auth (tvbuff_t *tvb, packet_info *pinfo, int offset,
/* Session Key */
offset = dissect_ntlmssp_blob(tvb, offset, ntlmssp_tree,
hf_ntlmssp_auth_sesskey,
- &item_end);
+ &item_end, NULL);
data_end = MAX(data_end, item_end);
}
@@ -768,19 +932,181 @@ dissect_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
/*
+ * Get the encryption state tied to this conversation. cryptpeer indicates
+ * whether to retrieve the data for peer1 or peer2.
+ */
+static rc4_state_struct *
+get_encrypted_state(packet_info *pinfo, int cryptpeer)
+{
+ conversation_t *conversation;
+ ntlmssp_info *conv_ntlmssp_info;
+
+ conversation = find_conversation(&pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ pinfo->destport, 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 = 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_state_peer1;
+ } else {
+ return &conv_ntlmssp_info->rc4_state_peer2;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
* See page 45 of "DCE/RPC over SMB" by Luke Kenneth Casson Leighton.
*/
+static void
+decrypt_verifier(tvbuff_t *tvb, int offset, guint32 encrypted_block_length,
+ packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *decr_tree = NULL;
+ proto_item *tf = NULL;
+ conversation_t *conversation;
+ rc4_state_struct *rc4_state;
+ rc4_state_struct *rc4_state_peer;
+ tvbuff_t *decr_tvb; /* Used to display decrypted buffer */
+ guint8 *peer_block;
+ ntlmssp_info *conv_ntlmssp_info = NULL;
+ ntlmssp_packet_info *packet_ntlmssp_info = NULL;
+ int decrypted_offset = 0;
+
+ packet_ntlmssp_info = p_get_proto_data(pinfo->fd, proto_ntlmssp);
+ if (packet_ntlmssp_info == NULL) {
+ /* We don't have data for this packet */
+ return;
+ }
+ if (!packet_ntlmssp_info->verifier_decrypted) {
+ conversation = find_conversation(&pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ pinfo->destport, 0);
+ if (conversation == NULL) {
+ /* There is no conversation, thus no encryption state */
+ return;
+ }
+
+ conv_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 (conv_ntlmssp_info->rc4_state_initialized != 1 ) {
+ /* The crypto sybsystem 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->peer1_dest_port == pinfo->destport) {
+ rc4_state = get_encrypted_state(pinfo, 1);
+ rc4_state_peer = get_encrypted_state(pinfo, 0);
+ } else {
+ rc4_state = get_encrypted_state(pinfo, 0);
+ rc4_state_peer = get_encrypted_state(pinfo, 1);
+ }
+
+ if (rc4_state == NULL || rc4_state_peer == NULL) {
+ /* There is no encryption state, so we cannot decrypt */
+ return;
+ }
+
+ /* Setup the buffer to decrypt to */
+ tvb_memcpy(tvb, packet_ntlmssp_info->verifier,
+ offset, encrypted_block_length);
+
+ /* Do the actual decryption of the verifier */
+ crypt_rc4(rc4_state, packet_ntlmssp_info->verifier,
+ encrypted_block_length);
+
+ /* We setup a temporary buffer so we can re-encrypt the payload after
+ decryption. This is to update the opposite peer's RC4 state */
+ peer_block = g_malloc(encrypted_block_length);
+ memcpy(peer_block, packet_ntlmssp_info->verifier,
+ encrypted_block_length);
+ crypt_rc4(rc4_state_peer, peer_block, encrypted_block_length);
+ g_free(peer_block);
+
+ /* 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_real_data(packet_ntlmssp_info->verifier,
+ encrypted_block_length,
+ encrypted_block_length);
+ tvb_set_child_real_data_tvbuff(tvb, decr_tvb);
+ add_new_data_source(pinfo, decr_tvb,
+ "Decrypted NTLMSSP Verifier");
+
+ /* Show the decrypted payload in the tree */
+ tf = proto_tree_add_text(tree, decr_tvb, 0, -1,
+ "Decrypted Verifier (%d byte%s)",
+ encrypted_block_length,
+ plurality(encrypted_block_length, "", "s"));
+ decr_tree = proto_item_add_subtree (tf, ett_ntlmssp);
+
+ /* LKCL page 45 says this is a "reserved" field. I'm not sure if it's
+ garbage because it's some sort of nonce, or because there is a problem
+ with the verifier decryption routine. */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_unknown1,
+ decr_tvb, decrypted_offset, 4, TRUE);
+ decrypted_offset += 4;
+
+ /* CRC32 of the DCE fragment data */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_crc32,
+ decr_tvb, decrypted_offset, 4, TRUE);
+ decrypted_offset += 4;
+
+ /* Incrementing sequence number of DCE conversation */
+ proto_tree_add_item (decr_tree, hf_ntlmssp_verf_sequence,
+ decr_tvb, decrypted_offset, 4, TRUE);
+ decrypted_offset += 4;
+}
+
static int
dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
volatile int offset = 0;
proto_tree *volatile ntlmssp_tree = NULL;
proto_item *tf = NULL;
+ guint32 verifier_length;
+ guint32 encrypted_block_length;
+
+ verifier_length = tvb_length_remaining (tvb, offset);
+ 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,
+ hf_ntlmssp_verf,
tvb, offset, -1, FALSE);
ntlmssp_tree = proto_item_add_subtree (tf,
@@ -804,11 +1130,15 @@ dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_vers,
tvb, offset, 4, TRUE);
offset += 4;
-
+
/* Encrypted body */
proto_tree_add_item (ntlmssp_tree, hf_ntlmssp_verf_body,
- tvb, offset, 12, TRUE);
- offset += 12;
+ tvb, offset, encrypted_block_length, TRUE);
+
+ /* Try to decrypt */
+ decrypt_verifier (tvb, offset, encrypted_block_length, pinfo, ntlmssp_tree);
+
+ offset += encrypted_block_length;
} CATCH(BoundsError) {
RETHROW;
} CATCH(ReportedBoundsError) {
@@ -818,14 +1148,108 @@ dissect_ntlmssp_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
return offset;
}
+static int
+dissect_ntlmssp_encrypted_payload(tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree)
+{
+ volatile int offset = 0;
+ tvbuff_t *decr_tvb; /* Used to display decrypted buffer */
+ guint8 *peer_block;
+ conversation_t *conversation;
+ guint32 encrypted_block_length;
+ rc4_state_struct *rc4_state;
+ rc4_state_struct *rc4_state_peer;
+ ntlmssp_info *conv_ntlmssp_info = NULL;
+ ntlmssp_packet_info *packet_ntlmssp_info = NULL;
+
+ encrypted_block_length = tvb_length_remaining (tvb, offset);
+
+ /* Check to see if we already have state for this packet */
+ packet_ntlmssp_info = p_get_proto_data(pinfo->fd, proto_ntlmssp);
+ if (packet_ntlmssp_info == NULL) {
+ /* We don't have any packet state, so create one */
+ packet_ntlmssp_info = g_malloc(sizeof(ntlmssp_packet_info));
+ memset(packet_ntlmssp_info, 0, sizeof(ntlmssp_packet_info));
+ p_add_proto_data(pinfo->fd, proto_ntlmssp, packet_ntlmssp_info);
+ }
+
+ if (!packet_ntlmssp_info->payload_decrypted) {
+ /* Pull the challenge info from the conversation */
+ conversation = find_conversation(&pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ pinfo->destport, 0);
+ if (conversation == NULL) {
+ /* There is no conversation, thus no encryption state */
+ return offset + encrypted_block_length;
+ }
+
+ conv_ntlmssp_info = conversation_get_proto_data(conversation,
+ proto_ntlmssp);
+ if (conv_ntlmssp_info == NULL) {
+ /* There is no NTLMSSP state tied to the conversation */
+ return offset + encrypted_block_length;
+ }
+
+ /* 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->peer1_dest_port == pinfo->destport) {
+ rc4_state = get_encrypted_state(pinfo, 1);
+ rc4_state_peer = get_encrypted_state(pinfo, 0);
+ } else {
+ rc4_state = get_encrypted_state(pinfo, 0);
+ rc4_state_peer = get_encrypted_state(pinfo, 1);
+ }
+
+ if (rc4_state == NULL || rc4_state_peer == NULL) {
+ /* There is no encryption state, so we cannot decrypt */
+ return offset + encrypted_block_length;
+ }
+
+ /* Store the decrypted contents in the packet state struct
+ (of course at this point, they aren't decrypted yet) */
+ tvb_memcpy(tvb, packet_ntlmssp_info->decrypted_payload,
+ offset, encrypted_block_length);
+
+ /* Do the decryption of the payload */
+ crypt_rc4(rc4_state, packet_ntlmssp_info->decrypted_payload,
+ encrypted_block_length);
+
+ /* We setup a temporary buffer so we can re-encrypt the payload after
+ decryption. This is to update the opposite peer's RC4 state */
+ peer_block = g_malloc(encrypted_block_length);
+ memcpy(peer_block, packet_ntlmssp_info->decrypted_payload,
+ encrypted_block_length);
+ crypt_rc4(rc4_state_peer, peer_block, encrypted_block_length);
+ g_free(peer_block);
+
+ packet_ntlmssp_info->payload_decrypted = TRUE;
+ }
+
+ /* Show the decrypted buffer in a new window */
+ decr_tvb = tvb_new_real_data(packet_ntlmssp_info->decrypted_payload,
+ encrypted_block_length,
+ encrypted_block_length);
+ tvb_set_child_real_data_tvbuff(tvb, decr_tvb);
+ add_new_data_source(pinfo, decr_tvb,
+ "Decrypted NTLMSSP block");
+
+ /* Show the decrypted payload in the tree */
+ if (tree) {
+ proto_tree_add_text(tree, decr_tvb, 0, -1,
+ "Decrypted stub data (%d byte%s)",
+ encrypted_block_length,
+ plurality(encrypted_block_length, "", "s"));
+ }
+
+ offset += encrypted_block_length;
+ return offset;
+}
+
static void
ntlmssp_init_protocol(void)
{
- if (flag_info_chunk)
- g_mem_chunk_destroy(flag_info_chunk);
- flag_info_chunk = g_mem_chunk_new("flag_info", sizeof(guint32 *),
- flag_info_chunk_count * sizeof(guint32 *),
- G_ALLOC_ONLY);
+
}
void
@@ -972,10 +1396,20 @@ proto_register_ntlmssp(void)
{ &hf_ntlmssp_address_list_domain_dns,
{ "Domain DNS Name", "ntlmssp.challenge.addresslist.domaindns", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL }},
+ { &hf_ntlmssp_verf,
+ { "NTLMSSP Verifier", "ntlmssp.verf", FT_NONE, BASE_NONE, NULL, 0x0, "NTLMSSP Verifier", HFILL }},
{ &hf_ntlmssp_verf_vers,
{ "Version Number", "ntlmssp.verf.vers", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
{ &hf_ntlmssp_verf_body,
- { "Verifier Body", "ntlmssp.verf.body", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }}
+ { "Verifier Body", "ntlmssp.verf.body", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }},
+ { &hf_ntlmssp_decrypted_payload,
+ { "NTLM Decrypted Payload", "ntlmssp.decrypted_payload", FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL }},
+ { &hf_ntlmssp_verf_unknown1,
+ { "Unknown 1", "ntlmssp.verf.unknown1", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
+ { &hf_ntlmssp_verf_crc32,
+ { "Verifier CRC32", "ntlmssp.verf.crc32", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
+ { &hf_ntlmssp_verf_sequence,
+ { "Verifier Sequence Number", "ntlmssp.verf.sequence", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }}
};
@@ -987,7 +1421,8 @@ proto_register_ntlmssp(void)
&ett_ntlmssp_blob,
&ett_ntlmssp_address_list
};
-
+ module_t *ntlmssp_module;
+
proto_ntlmssp = proto_register_protocol (
"NTLM Secure Service Provider", /* name */
"NTLMSSP", /* short name */
@@ -997,8 +1432,17 @@ proto_register_ntlmssp(void)
proto_register_subtree_array (ett, array_length (ett));
register_init_routine(&ntlmssp_init_protocol);
+ ntlmssp_module = prefs_register_protocol(proto_ntlmssp, NULL);
+
+ prefs_register_string_preference(ntlmssp_module, "nt_password",
+ "NT Password",
+ "NT Password (used to decrypt payloads)",
+ &nt_password);
+
register_dissector("ntlmssp", dissect_ntlmssp, proto_ntlmssp);
new_register_dissector("ntlmssp_verf", dissect_ntlmssp_verf, proto_ntlmssp);
+ new_register_dissector("ntlmssp_encrypted_payload",
+ dissect_ntlmssp_encrypted_payload, proto_ntlmssp);
}
void