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