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