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