1 /* vim: set ts=2 sw=2 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <ctype.h>
9 
10 #include "plbase64.h"
11 #include "nsStringAPI.h"
12 #include "prmem.h"
13 
14 /*
15  * ReadNTLM : reads NTLM messages.
16  *
17  * based on http://davenport.sourceforge.net/ntlm.html
18  */
19 
20 #define kNegotiateUnicode               0x00000001
21 #define kNegotiateOEM                   0x00000002
22 #define kRequestTarget                  0x00000004
23 #define kUnknown1                       0x00000008
24 #define kNegotiateSign                  0x00000010
25 #define kNegotiateSeal                  0x00000020
26 #define kNegotiateDatagramStyle         0x00000040
27 #define kNegotiateLanManagerKey         0x00000080
28 #define kNegotiateNetware               0x00000100
29 #define kNegotiateNTLMKey               0x00000200
30 #define kUnknown2                       0x00000400
31 #define kUnknown3                       0x00000800
32 #define kNegotiateDomainSupplied        0x00001000
33 #define kNegotiateWorkstationSupplied   0x00002000
34 #define kNegotiateLocalCall             0x00004000
35 #define kNegotiateAlwaysSign            0x00008000
36 #define kTargetTypeDomain               0x00010000
37 #define kTargetTypeServer               0x00020000
38 #define kTargetTypeShare                0x00040000
39 #define kNegotiateNTLM2Key              0x00080000
40 #define kRequestInitResponse            0x00100000
41 #define kRequestAcceptResponse          0x00200000
42 #define kRequestNonNTSessionKey         0x00400000
43 #define kNegotiateTargetInfo            0x00800000
44 #define kUnknown4                       0x01000000
45 #define kUnknown5                       0x02000000
46 #define kUnknown6                       0x04000000
47 #define kUnknown7                       0x08000000
48 #define kUnknown8                       0x10000000
49 #define kNegotiate128                   0x20000000
50 #define kNegotiateKeyExchange           0x40000000
51 #define kNegotiate56                    0x80000000
52 
53 static const char NTLM_SIGNATURE[] = "NTLMSSP";
54 static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 };
55 static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 };
56 static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 };
57 
58 #define NTLM_MARKER_LEN 4
59 #define NTLM_TYPE1_HEADER_LEN 32
60 #define NTLM_TYPE2_HEADER_LEN 32
61 #define NTLM_TYPE3_HEADER_LEN 64
62 
63 #define LM_HASH_LEN 16
64 #define LM_RESP_LEN 24
65 
66 #define NTLM_HASH_LEN 16
67 #define NTLM_RESP_LEN 24
68 
PrintFlags(uint32_t flags)69 static void PrintFlags(uint32_t flags)
70 {
71 #define TEST(_flag) \
72   if (flags & k ## _flag) \
73     printf("    0x%08x (" # _flag ")\n", k ## _flag)
74 
75   TEST(NegotiateUnicode);
76   TEST(NegotiateOEM);
77   TEST(RequestTarget);
78   TEST(Unknown1);
79   TEST(NegotiateSign);
80   TEST(NegotiateSeal);
81   TEST(NegotiateDatagramStyle);
82   TEST(NegotiateLanManagerKey);
83   TEST(NegotiateNetware);
84   TEST(NegotiateNTLMKey);
85   TEST(Unknown2);
86   TEST(Unknown3);
87   TEST(NegotiateDomainSupplied);
88   TEST(NegotiateWorkstationSupplied);
89   TEST(NegotiateLocalCall);
90   TEST(NegotiateAlwaysSign);
91   TEST(TargetTypeDomain);
92   TEST(TargetTypeServer);
93   TEST(TargetTypeShare);
94   TEST(NegotiateNTLM2Key);
95   TEST(RequestInitResponse);
96   TEST(RequestAcceptResponse);
97   TEST(RequestNonNTSessionKey);
98   TEST(NegotiateTargetInfo);
99   TEST(Unknown4);
100   TEST(Unknown5);
101   TEST(Unknown6);
102   TEST(Unknown7);
103   TEST(Unknown8);
104   TEST(Negotiate128);
105   TEST(NegotiateKeyExchange);
106   TEST(Negotiate56);
107 
108 #undef TEST
109 }
110 
111 static void
PrintBuf(const char * tag,const uint8_t * buf,uint32_t bufLen)112 PrintBuf(const char *tag, const uint8_t *buf, uint32_t bufLen)
113 {
114   int i;
115 
116   printf("%s =\n", tag);
117   while (bufLen > 0)
118   {
119     int count = bufLen;
120     if (count > 8)
121       count = 8;
122 
123     printf("    ");
124     for (i=0; i<count; ++i)
125     {
126       printf("0x%02x ", int(buf[i]));
127     }
128     for (; i<8; ++i)
129     {
130       printf("     ");
131     }
132 
133     printf("   ");
134     for (i=0; i<count; ++i)
135     {
136       if (isprint(buf[i]))
137         printf("%c", buf[i]);
138       else
139         printf(".");
140     }
141     printf("\n");
142 
143     bufLen -= count;
144     buf += count;
145   }
146 }
147 
148 static uint16_t
ReadUint16(const uint8_t * & buf)149 ReadUint16(const uint8_t *&buf)
150 {
151   uint16_t x;
152 #ifdef IS_BIG_ENDIAN
153   x = ((uint16_t) buf[1]) | ((uint16_t) buf[0] << 8);
154 #else
155   x = ((uint16_t) buf[0]) | ((uint16_t) buf[1] << 8);
156 #endif
157   buf += sizeof(x);
158   return x;
159 }
160 
161 static uint32_t
ReadUint32(const uint8_t * & buf)162 ReadUint32(const uint8_t *&buf)
163 {
164   uint32_t x;
165 #ifdef IS_BIG_ENDIAN
166   x = ( (uint32_t) buf[3])        |
167       (((uint32_t) buf[2]) << 8)  |
168       (((uint32_t) buf[1]) << 16) |
169       (((uint32_t) buf[0]) << 24);
170 #else
171   x = ( (uint32_t) buf[0])        |
172       (((uint32_t) buf[1]) << 8)  |
173       (((uint32_t) buf[2]) << 16) |
174       (((uint32_t) buf[3]) << 24);
175 #endif
176   buf += sizeof(x);
177   return x;
178 }
179 
180 typedef struct {
181   uint16_t length;
182   uint16_t capacity;
183   uint32_t offset;
184 } SecBuf;
185 
186 static void
ReadSecBuf(SecBuf * s,const uint8_t * & buf)187 ReadSecBuf(SecBuf *s, const uint8_t *&buf)
188 {
189   s->length = ReadUint16(buf);
190   s->capacity = ReadUint16(buf);
191   s->offset = ReadUint32(buf);
192 }
193 
194 static void
ReadType1MsgBody(const uint8_t * inBuf,uint32_t start)195 ReadType1MsgBody(const uint8_t *inBuf, uint32_t start)
196 {
197   const uint8_t *cursor = inBuf + start;
198   uint32_t flags;
199 
200   PrintBuf("flags", cursor, 4);
201   // read flags
202   flags = ReadUint32(cursor);
203   PrintFlags(flags);
204 
205   // type 1 message may not include trailing security buffers
206   if ((flags & kNegotiateDomainSupplied) |
207       (flags & kNegotiateWorkstationSupplied))
208   {
209     SecBuf secbuf;
210     ReadSecBuf(&secbuf, cursor);
211     PrintBuf("supplied domain", inBuf + secbuf.offset, secbuf.length);
212 
213     ReadSecBuf(&secbuf, cursor);
214     PrintBuf("supplied workstation", inBuf + secbuf.offset, secbuf.length);
215   }
216 }
217 
218 static void
ReadType2MsgBody(const uint8_t * inBuf,uint32_t start)219 ReadType2MsgBody(const uint8_t *inBuf, uint32_t start)
220 {
221   uint16_t targetLen, offset;
222   uint32_t flags;
223   const uint8_t *target;
224   const uint8_t *cursor = inBuf + start;
225 
226   // read target name security buffer
227   targetLen = ReadUint16(cursor);
228   ReadUint16(cursor); // discard next 16-bit value
229   offset = ReadUint32(cursor); // get offset from inBuf
230   target = inBuf + offset;
231 
232   PrintBuf("target", target, targetLen);
233 
234   PrintBuf("flags", cursor, 4);
235   // read flags
236   flags = ReadUint32(cursor);
237   PrintFlags(flags);
238 
239   // read challenge
240   PrintBuf("challenge", cursor, 8);
241   cursor += 8;
242 
243   PrintBuf("context", cursor, 8);
244   cursor += 8;
245 
246   SecBuf secbuf;
247   ReadSecBuf(&secbuf, cursor);
248   PrintBuf("target information", inBuf + secbuf.offset, secbuf.length);
249 }
250 
251 static void
ReadType3MsgBody(const uint8_t * inBuf,uint32_t start)252 ReadType3MsgBody(const uint8_t *inBuf, uint32_t start)
253 {
254   const uint8_t *cursor = inBuf + start;
255 
256   SecBuf secbuf;
257 
258   ReadSecBuf(&secbuf, cursor); // LM response
259   PrintBuf("LM response", inBuf + secbuf.offset, secbuf.length);
260 
261   ReadSecBuf(&secbuf, cursor); // NTLM response
262   PrintBuf("NTLM response", inBuf + secbuf.offset, secbuf.length);
263 
264   ReadSecBuf(&secbuf, cursor); // domain name
265   PrintBuf("domain name", inBuf + secbuf.offset, secbuf.length);
266 
267   ReadSecBuf(&secbuf, cursor); // user name
268   PrintBuf("user name", inBuf + secbuf.offset, secbuf.length);
269 
270   ReadSecBuf(&secbuf, cursor); // workstation name
271   PrintBuf("workstation name", inBuf + secbuf.offset, secbuf.length);
272 
273   ReadSecBuf(&secbuf, cursor); // session key
274   PrintBuf("session key", inBuf + secbuf.offset, secbuf.length);
275 
276   uint32_t flags = ReadUint32(cursor);
277   PrintBuf("flags", (const uint8_t *) &flags, sizeof(flags));
278   PrintFlags(flags);
279 }
280 
281 static void
ReadMsg(const char * base64buf,uint32_t bufLen)282 ReadMsg(const char *base64buf, uint32_t bufLen)
283 {
284   uint8_t *inBuf = (uint8_t *) PL_Base64Decode(base64buf, bufLen, nullptr);
285   if (!inBuf)
286   {
287     printf("PL_Base64Decode failed\n");
288     return;
289   }
290 
291   const uint8_t *cursor = inBuf;
292 
293   PrintBuf("signature", cursor, 8);
294 
295   // verify NTLMSSP signature
296   if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0)
297   {
298     printf("### invalid or corrupt NTLM signature\n");
299   }
300   cursor += sizeof(NTLM_SIGNATURE);
301 
302   PrintBuf("message type", cursor, 4);
303 
304   if (memcmp(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_MARKER_LEN)) == 0)
305     ReadType1MsgBody(inBuf, 12);
306   else if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_MARKER_LEN)) == 0)
307     ReadType2MsgBody(inBuf, 12);
308   else if (memcmp(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_MARKER_LEN)) == 0)
309     ReadType3MsgBody(inBuf, 12);
310   else
311     printf("### invalid or unknown message type\n");
312 
313   PR_Free(inBuf);
314 }
315 
main(int argc,char ** argv)316 int main(int argc, char **argv)
317 {
318   if (argc == 1)
319   {
320     printf("usage: ntlmread <msg>\n");
321     return -1;
322   }
323   ReadMsg(argv[1], (uint32_t) strlen(argv[1]));
324   return 0;
325 }
326