2003-11-18 05:20:34 +03:00
|
|
|
/* vim: set ts=2 sw=2 et cindent: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2003-11-18 05:20:34 +03:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2005-11-08 22:23:00 +03:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2003-11-18 05:20:34 +03:00
|
|
|
#include "plbase64.h"
|
2005-11-08 22:23:00 +03:00
|
|
|
#include "nsStringAPI.h"
|
2003-11-18 05:20:34 +03:00
|
|
|
#include "prmem.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ReadNTLM : reads NTLM messages.
|
|
|
|
*
|
|
|
|
* based on http://davenport.sourceforge.net/ntlm.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define kNegotiateUnicode 0x00000001
|
|
|
|
#define kNegotiateOEM 0x00000002
|
|
|
|
#define kRequestTarget 0x00000004
|
|
|
|
#define kUnknown1 0x00000008
|
|
|
|
#define kNegotiateSign 0x00000010
|
|
|
|
#define kNegotiateSeal 0x00000020
|
|
|
|
#define kNegotiateDatagramStyle 0x00000040
|
|
|
|
#define kNegotiateLanManagerKey 0x00000080
|
|
|
|
#define kNegotiateNetware 0x00000100
|
|
|
|
#define kNegotiateNTLMKey 0x00000200
|
|
|
|
#define kUnknown2 0x00000400
|
|
|
|
#define kUnknown3 0x00000800
|
|
|
|
#define kNegotiateDomainSupplied 0x00001000
|
|
|
|
#define kNegotiateWorkstationSupplied 0x00002000
|
|
|
|
#define kNegotiateLocalCall 0x00004000
|
|
|
|
#define kNegotiateAlwaysSign 0x00008000
|
|
|
|
#define kTargetTypeDomain 0x00010000
|
|
|
|
#define kTargetTypeServer 0x00020000
|
|
|
|
#define kTargetTypeShare 0x00040000
|
|
|
|
#define kNegotiateNTLM2Key 0x00080000
|
|
|
|
#define kRequestInitResponse 0x00100000
|
|
|
|
#define kRequestAcceptResponse 0x00200000
|
|
|
|
#define kRequestNonNTSessionKey 0x00400000
|
|
|
|
#define kNegotiateTargetInfo 0x00800000
|
|
|
|
#define kUnknown4 0x01000000
|
|
|
|
#define kUnknown5 0x02000000
|
|
|
|
#define kUnknown6 0x04000000
|
|
|
|
#define kUnknown7 0x08000000
|
|
|
|
#define kUnknown8 0x10000000
|
|
|
|
#define kNegotiate128 0x20000000
|
|
|
|
#define kNegotiateKeyExchange 0x40000000
|
|
|
|
#define kNegotiate56 0x80000000
|
|
|
|
|
|
|
|
static const char NTLM_SIGNATURE[] = "NTLMSSP";
|
|
|
|
static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 };
|
|
|
|
static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 };
|
|
|
|
static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 };
|
|
|
|
|
|
|
|
#define NTLM_MARKER_LEN 4
|
|
|
|
#define NTLM_TYPE1_HEADER_LEN 32
|
|
|
|
#define NTLM_TYPE2_HEADER_LEN 32
|
|
|
|
#define NTLM_TYPE3_HEADER_LEN 64
|
|
|
|
|
|
|
|
#define LM_HASH_LEN 16
|
|
|
|
#define LM_RESP_LEN 24
|
|
|
|
|
|
|
|
#define NTLM_HASH_LEN 16
|
|
|
|
#define NTLM_RESP_LEN 24
|
|
|
|
|
|
|
|
static void PrintFlags(PRUint32 flags)
|
|
|
|
{
|
|
|
|
#define TEST(_flag) \
|
|
|
|
if (flags & k ## _flag) \
|
|
|
|
printf(" 0x%08x (" # _flag ")\n", k ## _flag)
|
|
|
|
|
|
|
|
TEST(NegotiateUnicode);
|
|
|
|
TEST(NegotiateOEM);
|
|
|
|
TEST(RequestTarget);
|
|
|
|
TEST(Unknown1);
|
|
|
|
TEST(NegotiateSign);
|
|
|
|
TEST(NegotiateSeal);
|
|
|
|
TEST(NegotiateDatagramStyle);
|
|
|
|
TEST(NegotiateLanManagerKey);
|
|
|
|
TEST(NegotiateNetware);
|
|
|
|
TEST(NegotiateNTLMKey);
|
|
|
|
TEST(Unknown2);
|
|
|
|
TEST(Unknown3);
|
|
|
|
TEST(NegotiateDomainSupplied);
|
|
|
|
TEST(NegotiateWorkstationSupplied);
|
|
|
|
TEST(NegotiateLocalCall);
|
|
|
|
TEST(NegotiateAlwaysSign);
|
|
|
|
TEST(TargetTypeDomain);
|
|
|
|
TEST(TargetTypeServer);
|
|
|
|
TEST(TargetTypeShare);
|
|
|
|
TEST(NegotiateNTLM2Key);
|
|
|
|
TEST(RequestInitResponse);
|
|
|
|
TEST(RequestAcceptResponse);
|
|
|
|
TEST(RequestNonNTSessionKey);
|
|
|
|
TEST(NegotiateTargetInfo);
|
|
|
|
TEST(Unknown4);
|
|
|
|
TEST(Unknown5);
|
|
|
|
TEST(Unknown6);
|
|
|
|
TEST(Unknown7);
|
|
|
|
TEST(Unknown8);
|
|
|
|
TEST(Negotiate128);
|
|
|
|
TEST(NegotiateKeyExchange);
|
|
|
|
TEST(Negotiate56);
|
|
|
|
|
|
|
|
#undef TEST
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
PrintBuf(const char *tag, const PRUint8 *buf, PRUint32 bufLen)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("%s =\n", tag);
|
|
|
|
while (bufLen > 0)
|
|
|
|
{
|
|
|
|
int count = bufLen;
|
|
|
|
if (count > 8)
|
|
|
|
count = 8;
|
|
|
|
|
|
|
|
printf(" ");
|
|
|
|
for (i=0; i<count; ++i)
|
|
|
|
{
|
|
|
|
printf("0x%02x ", int(buf[i]));
|
|
|
|
}
|
|
|
|
for (; i<8; ++i)
|
|
|
|
{
|
|
|
|
printf(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(" ");
|
|
|
|
for (i=0; i<count; ++i)
|
|
|
|
{
|
|
|
|
if (isprint(buf[i]))
|
|
|
|
printf("%c", buf[i]);
|
|
|
|
else
|
|
|
|
printf(".");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
bufLen -= count;
|
|
|
|
buf += count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRUint16
|
|
|
|
ReadUint16(const PRUint8 *&buf)
|
|
|
|
{
|
|
|
|
PRUint16 x;
|
|
|
|
#ifdef IS_BIG_ENDIAN
|
|
|
|
x = ((PRUint16) buf[1]) | ((PRUint16) buf[0] << 8);
|
|
|
|
#else
|
|
|
|
x = ((PRUint16) buf[0]) | ((PRUint16) buf[1] << 8);
|
|
|
|
#endif
|
|
|
|
buf += sizeof(x);
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRUint32
|
|
|
|
ReadUint32(const PRUint8 *&buf)
|
|
|
|
{
|
|
|
|
PRUint32 x;
|
|
|
|
#ifdef IS_BIG_ENDIAN
|
|
|
|
x = ( (PRUint32) buf[3]) |
|
|
|
|
(((PRUint32) buf[2]) << 8) |
|
|
|
|
(((PRUint32) buf[1]) << 16) |
|
|
|
|
(((PRUint32) buf[0]) << 24);
|
|
|
|
#else
|
|
|
|
x = ( (PRUint32) buf[0]) |
|
|
|
|
(((PRUint32) buf[1]) << 8) |
|
|
|
|
(((PRUint32) buf[2]) << 16) |
|
|
|
|
(((PRUint32) buf[3]) << 24);
|
|
|
|
#endif
|
|
|
|
buf += sizeof(x);
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PRUint16 length;
|
|
|
|
PRUint16 capacity;
|
|
|
|
PRUint32 offset;
|
|
|
|
} SecBuf;
|
|
|
|
|
|
|
|
static void
|
|
|
|
ReadSecBuf(SecBuf *s, const PRUint8 *&buf)
|
|
|
|
{
|
|
|
|
s->length = ReadUint16(buf);
|
|
|
|
s->capacity = ReadUint16(buf);
|
|
|
|
s->offset = ReadUint32(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ReadType1MsgBody(const PRUint8 *inBuf, PRUint32 start)
|
|
|
|
{
|
|
|
|
const PRUint8 *cursor = inBuf + start;
|
|
|
|
PRUint32 flags;
|
|
|
|
|
|
|
|
PrintBuf("flags", cursor, 4);
|
|
|
|
// read flags
|
|
|
|
flags = ReadUint32(cursor);
|
|
|
|
PrintFlags(flags);
|
|
|
|
|
|
|
|
// type 1 message may not include trailing security buffers
|
|
|
|
if ((flags & kNegotiateDomainSupplied) |
|
|
|
|
(flags & kNegotiateWorkstationSupplied))
|
|
|
|
{
|
|
|
|
SecBuf secbuf;
|
|
|
|
ReadSecBuf(&secbuf, cursor);
|
|
|
|
PrintBuf("supplied domain", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor);
|
|
|
|
PrintBuf("supplied workstation", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ReadType2MsgBody(const PRUint8 *inBuf, PRUint32 start)
|
|
|
|
{
|
|
|
|
PRUint16 targetLen, offset;
|
|
|
|
PRUint32 flags;
|
|
|
|
const PRUint8 *target;
|
|
|
|
const PRUint8 *cursor = inBuf + start;
|
|
|
|
|
|
|
|
// read target name security buffer
|
|
|
|
targetLen = ReadUint16(cursor);
|
|
|
|
ReadUint16(cursor); // discard next 16-bit value
|
|
|
|
offset = ReadUint32(cursor); // get offset from inBuf
|
|
|
|
target = inBuf + offset;
|
|
|
|
|
|
|
|
PrintBuf("target", target, targetLen);
|
|
|
|
|
|
|
|
PrintBuf("flags", cursor, 4);
|
|
|
|
// read flags
|
|
|
|
flags = ReadUint32(cursor);
|
|
|
|
PrintFlags(flags);
|
|
|
|
|
|
|
|
// read challenge
|
|
|
|
PrintBuf("challenge", cursor, 8);
|
|
|
|
cursor += 8;
|
|
|
|
|
|
|
|
PrintBuf("context", cursor, 8);
|
|
|
|
cursor += 8;
|
|
|
|
|
|
|
|
SecBuf secbuf;
|
|
|
|
ReadSecBuf(&secbuf, cursor);
|
|
|
|
PrintBuf("target information", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ReadType3MsgBody(const PRUint8 *inBuf, PRUint32 start)
|
|
|
|
{
|
|
|
|
const PRUint8 *cursor = inBuf + start;
|
|
|
|
|
|
|
|
SecBuf secbuf;
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor); // LM response
|
|
|
|
PrintBuf("LM response", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor); // NTLM response
|
|
|
|
PrintBuf("NTLM response", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor); // domain name
|
|
|
|
PrintBuf("domain name", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor); // user name
|
|
|
|
PrintBuf("user name", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor); // workstation name
|
|
|
|
PrintBuf("workstation name", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
ReadSecBuf(&secbuf, cursor); // session key
|
|
|
|
PrintBuf("session key", inBuf + secbuf.offset, secbuf.length);
|
|
|
|
|
|
|
|
PRUint32 flags = ReadUint32(cursor);
|
|
|
|
PrintBuf("flags", (const PRUint8 *) &flags, sizeof(flags));
|
|
|
|
PrintFlags(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ReadMsg(const char *base64buf, PRUint32 bufLen)
|
|
|
|
{
|
|
|
|
PRUint8 *inBuf = (PRUint8 *) PL_Base64Decode(base64buf, bufLen, NULL);
|
|
|
|
if (!inBuf)
|
|
|
|
{
|
|
|
|
printf("PL_Base64Decode failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const PRUint8 *cursor = inBuf;
|
|
|
|
|
|
|
|
PrintBuf("signature", cursor, 8);
|
|
|
|
|
|
|
|
// verify NTLMSSP signature
|
|
|
|
if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0)
|
|
|
|
{
|
|
|
|
printf("### invalid or corrupt NTLM signature\n");
|
|
|
|
}
|
|
|
|
cursor += sizeof(NTLM_SIGNATURE);
|
|
|
|
|
|
|
|
PrintBuf("message type", cursor, 4);
|
|
|
|
|
|
|
|
if (memcmp(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_MARKER_LEN)) == 0)
|
|
|
|
ReadType1MsgBody(inBuf, 12);
|
|
|
|
else if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_MARKER_LEN)) == 0)
|
|
|
|
ReadType2MsgBody(inBuf, 12);
|
|
|
|
else if (memcmp(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_MARKER_LEN)) == 0)
|
|
|
|
ReadType3MsgBody(inBuf, 12);
|
|
|
|
else
|
|
|
|
printf("### invalid or unknown message type\n");
|
|
|
|
|
|
|
|
PR_Free(inBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (argc == 1)
|
|
|
|
{
|
|
|
|
printf("usage: ntlmread <msg>\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ReadMsg(argv[1], (PRUint32) strlen(argv[1]));
|
|
|
|
return 0;
|
|
|
|
}
|