|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 } |