Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "prlog.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "nsNTLMAuthModule.h" |
michael@0 | 9 | #include "nsNSSShutDown.h" |
michael@0 | 10 | #include "nsNativeCharsetUtils.h" |
michael@0 | 11 | #include "prsystem.h" |
michael@0 | 12 | #include "pk11pub.h" |
michael@0 | 13 | #include "md4.h" |
michael@0 | 14 | #include "mozilla/Likely.h" |
michael@0 | 15 | #include "mozilla/Telemetry.h" |
michael@0 | 16 | #include "mozilla/Preferences.h" |
michael@0 | 17 | |
michael@0 | 18 | #ifdef PR_LOGGING |
michael@0 | 19 | static PRLogModuleInfo * |
michael@0 | 20 | GetNTLMLog() |
michael@0 | 21 | { |
michael@0 | 22 | static PRLogModuleInfo *sNTLMLog; |
michael@0 | 23 | if (!sNTLMLog) |
michael@0 | 24 | sNTLMLog = PR_NewLogModule("NTLM"); |
michael@0 | 25 | return sNTLMLog; |
michael@0 | 26 | } |
michael@0 | 27 | |
michael@0 | 28 | #define LOG(x) PR_LOG(GetNTLMLog(), PR_LOG_DEBUG, x) |
michael@0 | 29 | #define LOG_ENABLED() PR_LOG_TEST(GetNTLMLog(), PR_LOG_DEBUG) |
michael@0 | 30 | #else |
michael@0 | 31 | #define LOG(x) |
michael@0 | 32 | #endif |
michael@0 | 33 | |
michael@0 | 34 | static void des_makekey(const uint8_t *raw, uint8_t *key); |
michael@0 | 35 | static void des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash); |
michael@0 | 36 | static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result); |
michael@0 | 37 | |
michael@0 | 38 | //----------------------------------------------------------------------------- |
michael@0 | 39 | // this file contains a cross-platform NTLM authentication implementation. it |
michael@0 | 40 | // is based on documentation from: http://davenport.sourceforge.net/ntlm.html |
michael@0 | 41 | //----------------------------------------------------------------------------- |
michael@0 | 42 | |
michael@0 | 43 | #define NTLM_NegotiateUnicode 0x00000001 |
michael@0 | 44 | #define NTLM_NegotiateOEM 0x00000002 |
michael@0 | 45 | #define NTLM_RequestTarget 0x00000004 |
michael@0 | 46 | #define NTLM_Unknown1 0x00000008 |
michael@0 | 47 | #define NTLM_NegotiateSign 0x00000010 |
michael@0 | 48 | #define NTLM_NegotiateSeal 0x00000020 |
michael@0 | 49 | #define NTLM_NegotiateDatagramStyle 0x00000040 |
michael@0 | 50 | #define NTLM_NegotiateLanManagerKey 0x00000080 |
michael@0 | 51 | #define NTLM_NegotiateNetware 0x00000100 |
michael@0 | 52 | #define NTLM_NegotiateNTLMKey 0x00000200 |
michael@0 | 53 | #define NTLM_Unknown2 0x00000400 |
michael@0 | 54 | #define NTLM_Unknown3 0x00000800 |
michael@0 | 55 | #define NTLM_NegotiateDomainSupplied 0x00001000 |
michael@0 | 56 | #define NTLM_NegotiateWorkstationSupplied 0x00002000 |
michael@0 | 57 | #define NTLM_NegotiateLocalCall 0x00004000 |
michael@0 | 58 | #define NTLM_NegotiateAlwaysSign 0x00008000 |
michael@0 | 59 | #define NTLM_TargetTypeDomain 0x00010000 |
michael@0 | 60 | #define NTLM_TargetTypeServer 0x00020000 |
michael@0 | 61 | #define NTLM_TargetTypeShare 0x00040000 |
michael@0 | 62 | #define NTLM_NegotiateNTLM2Key 0x00080000 |
michael@0 | 63 | #define NTLM_RequestInitResponse 0x00100000 |
michael@0 | 64 | #define NTLM_RequestAcceptResponse 0x00200000 |
michael@0 | 65 | #define NTLM_RequestNonNTSessionKey 0x00400000 |
michael@0 | 66 | #define NTLM_NegotiateTargetInfo 0x00800000 |
michael@0 | 67 | #define NTLM_Unknown4 0x01000000 |
michael@0 | 68 | #define NTLM_Unknown5 0x02000000 |
michael@0 | 69 | #define NTLM_Unknown6 0x04000000 |
michael@0 | 70 | #define NTLM_Unknown7 0x08000000 |
michael@0 | 71 | #define NTLM_Unknown8 0x10000000 |
michael@0 | 72 | #define NTLM_Negotiate128 0x20000000 |
michael@0 | 73 | #define NTLM_NegotiateKeyExchange 0x40000000 |
michael@0 | 74 | #define NTLM_Negotiate56 0x80000000 |
michael@0 | 75 | |
michael@0 | 76 | // we send these flags with our type 1 message |
michael@0 | 77 | #define NTLM_TYPE1_FLAGS \ |
michael@0 | 78 | (NTLM_NegotiateUnicode | \ |
michael@0 | 79 | NTLM_NegotiateOEM | \ |
michael@0 | 80 | NTLM_RequestTarget | \ |
michael@0 | 81 | NTLM_NegotiateNTLMKey | \ |
michael@0 | 82 | NTLM_NegotiateAlwaysSign | \ |
michael@0 | 83 | NTLM_NegotiateNTLM2Key) |
michael@0 | 84 | |
michael@0 | 85 | static const char NTLM_SIGNATURE[] = "NTLMSSP"; |
michael@0 | 86 | static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 }; |
michael@0 | 87 | static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 }; |
michael@0 | 88 | static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 }; |
michael@0 | 89 | |
michael@0 | 90 | #define NTLM_TYPE1_HEADER_LEN 32 |
michael@0 | 91 | #define NTLM_TYPE2_HEADER_LEN 32 |
michael@0 | 92 | #define NTLM_TYPE3_HEADER_LEN 64 |
michael@0 | 93 | |
michael@0 | 94 | #define LM_HASH_LEN 16 |
michael@0 | 95 | #define LM_RESP_LEN 24 |
michael@0 | 96 | |
michael@0 | 97 | #define NTLM_HASH_LEN 16 |
michael@0 | 98 | #define NTLM_RESP_LEN 24 |
michael@0 | 99 | |
michael@0 | 100 | //----------------------------------------------------------------------------- |
michael@0 | 101 | |
michael@0 | 102 | static bool sendLM = false; |
michael@0 | 103 | |
michael@0 | 104 | /*static*/ void |
michael@0 | 105 | nsNTLMAuthModule::SetSendLM(bool newSendLM) |
michael@0 | 106 | { |
michael@0 | 107 | sendLM = newSendLM; |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | //----------------------------------------------------------------------------- |
michael@0 | 111 | |
michael@0 | 112 | #ifdef PR_LOGGING |
michael@0 | 113 | |
michael@0 | 114 | /** |
michael@0 | 115 | * Prints a description of flags to the NSPR Log, if enabled. |
michael@0 | 116 | */ |
michael@0 | 117 | static void LogFlags(uint32_t flags) |
michael@0 | 118 | { |
michael@0 | 119 | if (!LOG_ENABLED()) |
michael@0 | 120 | return; |
michael@0 | 121 | #define TEST(_flag) \ |
michael@0 | 122 | if (flags & NTLM_ ## _flag) \ |
michael@0 | 123 | PR_LogPrint(" 0x%08x (" # _flag ")\n", NTLM_ ## _flag) |
michael@0 | 124 | |
michael@0 | 125 | TEST(NegotiateUnicode); |
michael@0 | 126 | TEST(NegotiateOEM); |
michael@0 | 127 | TEST(RequestTarget); |
michael@0 | 128 | TEST(Unknown1); |
michael@0 | 129 | TEST(NegotiateSign); |
michael@0 | 130 | TEST(NegotiateSeal); |
michael@0 | 131 | TEST(NegotiateDatagramStyle); |
michael@0 | 132 | TEST(NegotiateLanManagerKey); |
michael@0 | 133 | TEST(NegotiateNetware); |
michael@0 | 134 | TEST(NegotiateNTLMKey); |
michael@0 | 135 | TEST(Unknown2); |
michael@0 | 136 | TEST(Unknown3); |
michael@0 | 137 | TEST(NegotiateDomainSupplied); |
michael@0 | 138 | TEST(NegotiateWorkstationSupplied); |
michael@0 | 139 | TEST(NegotiateLocalCall); |
michael@0 | 140 | TEST(NegotiateAlwaysSign); |
michael@0 | 141 | TEST(TargetTypeDomain); |
michael@0 | 142 | TEST(TargetTypeServer); |
michael@0 | 143 | TEST(TargetTypeShare); |
michael@0 | 144 | TEST(NegotiateNTLM2Key); |
michael@0 | 145 | TEST(RequestInitResponse); |
michael@0 | 146 | TEST(RequestAcceptResponse); |
michael@0 | 147 | TEST(RequestNonNTSessionKey); |
michael@0 | 148 | TEST(NegotiateTargetInfo); |
michael@0 | 149 | TEST(Unknown4); |
michael@0 | 150 | TEST(Unknown5); |
michael@0 | 151 | TEST(Unknown6); |
michael@0 | 152 | TEST(Unknown7); |
michael@0 | 153 | TEST(Unknown8); |
michael@0 | 154 | TEST(Negotiate128); |
michael@0 | 155 | TEST(NegotiateKeyExchange); |
michael@0 | 156 | TEST(Negotiate56); |
michael@0 | 157 | |
michael@0 | 158 | #undef TEST |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | /** |
michael@0 | 162 | * Prints a hexdump of buf to the NSPR Log, if enabled. |
michael@0 | 163 | * @param tag Description of the data, will be printed in front of the data |
michael@0 | 164 | * @param buf the data to print |
michael@0 | 165 | * @param bufLen length of the data |
michael@0 | 166 | */ |
michael@0 | 167 | static void |
michael@0 | 168 | LogBuf(const char *tag, const uint8_t *buf, uint32_t bufLen) |
michael@0 | 169 | { |
michael@0 | 170 | int i; |
michael@0 | 171 | |
michael@0 | 172 | if (!LOG_ENABLED()) |
michael@0 | 173 | return; |
michael@0 | 174 | |
michael@0 | 175 | PR_LogPrint("%s =\n", tag); |
michael@0 | 176 | char line[80]; |
michael@0 | 177 | while (bufLen > 0) |
michael@0 | 178 | { |
michael@0 | 179 | int count = bufLen; |
michael@0 | 180 | if (count > 8) |
michael@0 | 181 | count = 8; |
michael@0 | 182 | |
michael@0 | 183 | strcpy(line, " "); |
michael@0 | 184 | for (i=0; i<count; ++i) |
michael@0 | 185 | { |
michael@0 | 186 | int len = strlen(line); |
michael@0 | 187 | PR_snprintf(line + len, sizeof(line) - len, "0x%02x ", int(buf[i])); |
michael@0 | 188 | } |
michael@0 | 189 | for (; i<8; ++i) |
michael@0 | 190 | { |
michael@0 | 191 | int len = strlen(line); |
michael@0 | 192 | PR_snprintf(line + len, sizeof(line) - len, " "); |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | int len = strlen(line); |
michael@0 | 196 | PR_snprintf(line + len, sizeof(line) - len, " "); |
michael@0 | 197 | for (i=0; i<count; ++i) |
michael@0 | 198 | { |
michael@0 | 199 | len = strlen(line); |
michael@0 | 200 | if (isprint(buf[i])) |
michael@0 | 201 | PR_snprintf(line + len, sizeof(line) - len, "%c", buf[i]); |
michael@0 | 202 | else |
michael@0 | 203 | PR_snprintf(line + len, sizeof(line) - len, "."); |
michael@0 | 204 | } |
michael@0 | 205 | PR_LogPrint("%s\n", line); |
michael@0 | 206 | |
michael@0 | 207 | bufLen -= count; |
michael@0 | 208 | buf += count; |
michael@0 | 209 | } |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | #include "plbase64.h" |
michael@0 | 213 | #include "prmem.h" |
michael@0 | 214 | /** |
michael@0 | 215 | * Print base64-encoded token to the NSPR Log. |
michael@0 | 216 | * @param name Description of the token, will be printed in front |
michael@0 | 217 | * @param token The token to print |
michael@0 | 218 | * @param tokenLen length of the data in token |
michael@0 | 219 | */ |
michael@0 | 220 | static void LogToken(const char *name, const void *token, uint32_t tokenLen) |
michael@0 | 221 | { |
michael@0 | 222 | if (!LOG_ENABLED()) |
michael@0 | 223 | return; |
michael@0 | 224 | |
michael@0 | 225 | char *b64data = PL_Base64Encode((const char *) token, tokenLen, nullptr); |
michael@0 | 226 | if (b64data) |
michael@0 | 227 | { |
michael@0 | 228 | PR_LogPrint("%s: %s\n", name, b64data); |
michael@0 | 229 | PR_Free(b64data); |
michael@0 | 230 | } |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | #else |
michael@0 | 234 | #define LogFlags(x) |
michael@0 | 235 | #define LogBuf(a,b,c) |
michael@0 | 236 | #define LogToken(a,b,c) |
michael@0 | 237 | |
michael@0 | 238 | #endif // PR_LOGGING |
michael@0 | 239 | |
michael@0 | 240 | //----------------------------------------------------------------------------- |
michael@0 | 241 | |
michael@0 | 242 | // byte order swapping |
michael@0 | 243 | #define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) |
michael@0 | 244 | #define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16))) |
michael@0 | 245 | |
michael@0 | 246 | static void * |
michael@0 | 247 | WriteBytes(void *buf, const void *data, uint32_t dataLen) |
michael@0 | 248 | { |
michael@0 | 249 | memcpy(buf, data, dataLen); |
michael@0 | 250 | return (uint8_t *) buf + dataLen; |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | static void * |
michael@0 | 254 | WriteDWORD(void *buf, uint32_t dword) |
michael@0 | 255 | { |
michael@0 | 256 | #ifdef IS_BIG_ENDIAN |
michael@0 | 257 | // NTLM uses little endian on the wire |
michael@0 | 258 | dword = SWAP32(dword); |
michael@0 | 259 | #endif |
michael@0 | 260 | return WriteBytes(buf, &dword, sizeof(dword)); |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | static void * |
michael@0 | 264 | WriteSecBuf(void *buf, uint16_t length, uint32_t offset) |
michael@0 | 265 | { |
michael@0 | 266 | #ifdef IS_BIG_ENDIAN |
michael@0 | 267 | length = SWAP16(length); |
michael@0 | 268 | offset = SWAP32(offset); |
michael@0 | 269 | #endif |
michael@0 | 270 | buf = WriteBytes(buf, &length, sizeof(length)); |
michael@0 | 271 | buf = WriteBytes(buf, &length, sizeof(length)); |
michael@0 | 272 | buf = WriteBytes(buf, &offset, sizeof(offset)); |
michael@0 | 273 | return buf; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | #ifdef IS_BIG_ENDIAN |
michael@0 | 277 | /** |
michael@0 | 278 | * WriteUnicodeLE copies a unicode string from one buffer to another. The |
michael@0 | 279 | * resulting unicode string is in little-endian format. The input string is |
michael@0 | 280 | * assumed to be in the native endianness of the local machine. It is safe |
michael@0 | 281 | * to pass the same buffer as both input and output, which is a handy way to |
michael@0 | 282 | * convert the unicode buffer to little-endian on big-endian platforms. |
michael@0 | 283 | */ |
michael@0 | 284 | static void * |
michael@0 | 285 | WriteUnicodeLE(void *buf, const char16_t *str, uint32_t strLen) |
michael@0 | 286 | { |
michael@0 | 287 | // convert input string from BE to LE |
michael@0 | 288 | uint8_t *cursor = (uint8_t *) buf, |
michael@0 | 289 | *input = (uint8_t *) str; |
michael@0 | 290 | for (uint32_t i=0; i<strLen; ++i, input+=2, cursor+=2) |
michael@0 | 291 | { |
michael@0 | 292 | // allow for the case where |buf == str| |
michael@0 | 293 | uint8_t temp = input[0]; |
michael@0 | 294 | cursor[0] = input[1]; |
michael@0 | 295 | cursor[1] = temp; |
michael@0 | 296 | } |
michael@0 | 297 | return buf; |
michael@0 | 298 | } |
michael@0 | 299 | #endif |
michael@0 | 300 | |
michael@0 | 301 | static uint16_t |
michael@0 | 302 | ReadUint16(const uint8_t *&buf) |
michael@0 | 303 | { |
michael@0 | 304 | uint16_t x = ((uint16_t) buf[0]) | ((uint16_t) buf[1] << 8); |
michael@0 | 305 | buf += sizeof(x); |
michael@0 | 306 | return x; |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | static uint32_t |
michael@0 | 310 | ReadUint32(const uint8_t *&buf) |
michael@0 | 311 | { |
michael@0 | 312 | uint32_t x = ( (uint32_t) buf[0]) | |
michael@0 | 313 | (((uint32_t) buf[1]) << 8) | |
michael@0 | 314 | (((uint32_t) buf[2]) << 16) | |
michael@0 | 315 | (((uint32_t) buf[3]) << 24); |
michael@0 | 316 | buf += sizeof(x); |
michael@0 | 317 | return x; |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | //----------------------------------------------------------------------------- |
michael@0 | 321 | |
michael@0 | 322 | static void |
michael@0 | 323 | ZapBuf(void *buf, size_t bufLen) |
michael@0 | 324 | { |
michael@0 | 325 | memset(buf, 0, bufLen); |
michael@0 | 326 | } |
michael@0 | 327 | |
michael@0 | 328 | static void |
michael@0 | 329 | ZapString(nsCString &s) |
michael@0 | 330 | { |
michael@0 | 331 | ZapBuf(s.BeginWriting(), s.Length()); |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | static void |
michael@0 | 335 | ZapString(nsString &s) |
michael@0 | 336 | { |
michael@0 | 337 | ZapBuf(s.BeginWriting(), s.Length() * 2); |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | static const unsigned char LM_MAGIC[] = "KGS!@#$%"; |
michael@0 | 341 | |
michael@0 | 342 | /** |
michael@0 | 343 | * LM_Hash computes the LM hash of the given password. |
michael@0 | 344 | * |
michael@0 | 345 | * @param password |
michael@0 | 346 | * null-terminated unicode password. |
michael@0 | 347 | * @param hash |
michael@0 | 348 | * 16-byte result buffer |
michael@0 | 349 | */ |
michael@0 | 350 | static void |
michael@0 | 351 | LM_Hash(const nsString &password, unsigned char *hash) |
michael@0 | 352 | { |
michael@0 | 353 | // convert password to OEM character set. we'll just use the native |
michael@0 | 354 | // filesystem charset. |
michael@0 | 355 | nsAutoCString passbuf; |
michael@0 | 356 | NS_CopyUnicodeToNative(password, passbuf); |
michael@0 | 357 | ToUpperCase(passbuf); |
michael@0 | 358 | uint32_t n = passbuf.Length(); |
michael@0 | 359 | passbuf.SetLength(14); |
michael@0 | 360 | for (uint32_t i=n; i<14; ++i) |
michael@0 | 361 | passbuf.SetCharAt('\0', i); |
michael@0 | 362 | |
michael@0 | 363 | unsigned char k1[8], k2[8]; |
michael@0 | 364 | des_makekey((const unsigned char *) passbuf.get() , k1); |
michael@0 | 365 | des_makekey((const unsigned char *) passbuf.get() + 7, k2); |
michael@0 | 366 | ZapString(passbuf); |
michael@0 | 367 | |
michael@0 | 368 | // use password keys to hash LM magic string twice. |
michael@0 | 369 | des_encrypt(k1, LM_MAGIC, hash); |
michael@0 | 370 | des_encrypt(k2, LM_MAGIC, hash + 8); |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | /** |
michael@0 | 374 | * NTLM_Hash computes the NTLM hash of the given password. |
michael@0 | 375 | * |
michael@0 | 376 | * @param password |
michael@0 | 377 | * null-terminated unicode password. |
michael@0 | 378 | * @param hash |
michael@0 | 379 | * 16-byte result buffer |
michael@0 | 380 | */ |
michael@0 | 381 | static void |
michael@0 | 382 | NTLM_Hash(const nsString &password, unsigned char *hash) |
michael@0 | 383 | { |
michael@0 | 384 | uint32_t len = password.Length(); |
michael@0 | 385 | uint8_t *passbuf; |
michael@0 | 386 | |
michael@0 | 387 | #ifdef IS_BIG_ENDIAN |
michael@0 | 388 | passbuf = (uint8_t *) malloc(len * 2); |
michael@0 | 389 | WriteUnicodeLE(passbuf, password.get(), len); |
michael@0 | 390 | #else |
michael@0 | 391 | passbuf = (uint8_t *) password.get(); |
michael@0 | 392 | #endif |
michael@0 | 393 | |
michael@0 | 394 | md4sum(passbuf, len * 2, hash); |
michael@0 | 395 | |
michael@0 | 396 | #ifdef IS_BIG_ENDIAN |
michael@0 | 397 | ZapBuf(passbuf, len * 2); |
michael@0 | 398 | free(passbuf); |
michael@0 | 399 | #endif |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | //----------------------------------------------------------------------------- |
michael@0 | 403 | |
michael@0 | 404 | /** |
michael@0 | 405 | * LM_Response generates the LM response given a 16-byte password hash and the |
michael@0 | 406 | * challenge from the Type-2 message. |
michael@0 | 407 | * |
michael@0 | 408 | * @param hash |
michael@0 | 409 | * 16-byte password hash |
michael@0 | 410 | * @param challenge |
michael@0 | 411 | * 8-byte challenge from Type-2 message |
michael@0 | 412 | * @param response |
michael@0 | 413 | * 24-byte buffer to contain the LM response upon return |
michael@0 | 414 | */ |
michael@0 | 415 | static void |
michael@0 | 416 | LM_Response(const uint8_t *hash, const uint8_t *challenge, uint8_t *response) |
michael@0 | 417 | { |
michael@0 | 418 | uint8_t keybytes[21], k1[8], k2[8], k3[8]; |
michael@0 | 419 | |
michael@0 | 420 | memcpy(keybytes, hash, 16); |
michael@0 | 421 | ZapBuf(keybytes + 16, 5); |
michael@0 | 422 | |
michael@0 | 423 | des_makekey(keybytes , k1); |
michael@0 | 424 | des_makekey(keybytes + 7, k2); |
michael@0 | 425 | des_makekey(keybytes + 14, k3); |
michael@0 | 426 | |
michael@0 | 427 | des_encrypt(k1, challenge, response); |
michael@0 | 428 | des_encrypt(k2, challenge, response + 8); |
michael@0 | 429 | des_encrypt(k3, challenge, response + 16); |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | //----------------------------------------------------------------------------- |
michael@0 | 433 | |
michael@0 | 434 | static nsresult |
michael@0 | 435 | GenerateType1Msg(void **outBuf, uint32_t *outLen) |
michael@0 | 436 | { |
michael@0 | 437 | // |
michael@0 | 438 | // verify that bufLen is sufficient |
michael@0 | 439 | // |
michael@0 | 440 | *outLen = NTLM_TYPE1_HEADER_LEN; |
michael@0 | 441 | *outBuf = nsMemory::Alloc(*outLen); |
michael@0 | 442 | if (!*outBuf) |
michael@0 | 443 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 444 | |
michael@0 | 445 | // |
michael@0 | 446 | // write out type 1 msg |
michael@0 | 447 | // |
michael@0 | 448 | void *cursor = *outBuf; |
michael@0 | 449 | |
michael@0 | 450 | // 0 : signature |
michael@0 | 451 | cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); |
michael@0 | 452 | |
michael@0 | 453 | // 8 : marker |
michael@0 | 454 | cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER)); |
michael@0 | 455 | |
michael@0 | 456 | // 12 : flags |
michael@0 | 457 | cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS); |
michael@0 | 458 | |
michael@0 | 459 | // |
michael@0 | 460 | // NOTE: it is common for the domain and workstation fields to be empty. |
michael@0 | 461 | // this is true of Win2k clients, and my guess is that there is |
michael@0 | 462 | // little utility to sending these strings before the charset has |
michael@0 | 463 | // been negotiated. we follow suite -- anyways, it doesn't hurt |
michael@0 | 464 | // to save some bytes on the wire ;-) |
michael@0 | 465 | // |
michael@0 | 466 | |
michael@0 | 467 | // 16 : supplied domain security buffer (empty) |
michael@0 | 468 | cursor = WriteSecBuf(cursor, 0, 0); |
michael@0 | 469 | |
michael@0 | 470 | // 24 : supplied workstation security buffer (empty) |
michael@0 | 471 | cursor = WriteSecBuf(cursor, 0, 0); |
michael@0 | 472 | |
michael@0 | 473 | return NS_OK; |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | struct Type2Msg |
michael@0 | 477 | { |
michael@0 | 478 | uint32_t flags; // NTLM_Xxx bitwise combination |
michael@0 | 479 | uint8_t challenge[8]; // 8 byte challenge |
michael@0 | 480 | const void *target; // target string (type depends on flags) |
michael@0 | 481 | uint32_t targetLen; // target length in bytes |
michael@0 | 482 | }; |
michael@0 | 483 | |
michael@0 | 484 | static nsresult |
michael@0 | 485 | ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) |
michael@0 | 486 | { |
michael@0 | 487 | // make sure inBuf is long enough to contain a meaningful type2 msg. |
michael@0 | 488 | // |
michael@0 | 489 | // 0 NTLMSSP Signature |
michael@0 | 490 | // 8 NTLM Message Type |
michael@0 | 491 | // 12 Target Name |
michael@0 | 492 | // 20 Flags |
michael@0 | 493 | // 24 Challenge |
michael@0 | 494 | // 32 end of header, start of optional data blocks |
michael@0 | 495 | // |
michael@0 | 496 | if (inLen < NTLM_TYPE2_HEADER_LEN) |
michael@0 | 497 | return NS_ERROR_UNEXPECTED; |
michael@0 | 498 | |
michael@0 | 499 | const uint8_t *cursor = (const uint8_t *) inBuf; |
michael@0 | 500 | |
michael@0 | 501 | // verify NTLMSSP signature |
michael@0 | 502 | if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0) |
michael@0 | 503 | return NS_ERROR_UNEXPECTED; |
michael@0 | 504 | |
michael@0 | 505 | cursor += sizeof(NTLM_SIGNATURE); |
michael@0 | 506 | |
michael@0 | 507 | // verify Type-2 marker |
michael@0 | 508 | if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0) |
michael@0 | 509 | return NS_ERROR_UNEXPECTED; |
michael@0 | 510 | |
michael@0 | 511 | cursor += sizeof(NTLM_TYPE2_MARKER); |
michael@0 | 512 | |
michael@0 | 513 | // Read target name security buffer: ... |
michael@0 | 514 | // ... read target length. |
michael@0 | 515 | uint32_t targetLen = ReadUint16(cursor); |
michael@0 | 516 | // ... skip next 16-bit "allocated space" value. |
michael@0 | 517 | ReadUint16(cursor); |
michael@0 | 518 | // ... read offset from inBuf. |
michael@0 | 519 | uint32_t offset = ReadUint32(cursor); |
michael@0 | 520 | // Check the offset / length combo is in range of the input buffer, including |
michael@0 | 521 | // integer overflow checking. |
michael@0 | 522 | if (MOZ_LIKELY(offset < offset + targetLen && offset + targetLen <= inLen)) { |
michael@0 | 523 | msg->targetLen = targetLen; |
michael@0 | 524 | msg->target = ((const uint8_t *) inBuf) + offset; |
michael@0 | 525 | } |
michael@0 | 526 | else |
michael@0 | 527 | { |
michael@0 | 528 | // Do not error out, for (conservative) backward compatibility. |
michael@0 | 529 | msg->targetLen = 0; |
michael@0 | 530 | msg->target = nullptr; |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | // read flags |
michael@0 | 534 | msg->flags = ReadUint32(cursor); |
michael@0 | 535 | |
michael@0 | 536 | // read challenge |
michael@0 | 537 | memcpy(msg->challenge, cursor, sizeof(msg->challenge)); |
michael@0 | 538 | cursor += sizeof(msg->challenge); |
michael@0 | 539 | |
michael@0 | 540 | |
michael@0 | 541 | LOG(("NTLM type 2 message:\n")); |
michael@0 | 542 | LogBuf("target", (const uint8_t *) msg->target, msg->targetLen); |
michael@0 | 543 | LogBuf("flags", (const uint8_t *) &msg->flags, 4); |
michael@0 | 544 | LogFlags(msg->flags); |
michael@0 | 545 | LogBuf("challenge", msg->challenge, sizeof(msg->challenge)); |
michael@0 | 546 | |
michael@0 | 547 | // we currently do not implement LMv2/NTLMv2 or NTLM2 responses, |
michael@0 | 548 | // so we can ignore target information. we may want to enable |
michael@0 | 549 | // support for these alternate mechanisms in the future. |
michael@0 | 550 | return NS_OK; |
michael@0 | 551 | } |
michael@0 | 552 | |
michael@0 | 553 | static nsresult |
michael@0 | 554 | GenerateType3Msg(const nsString &domain, |
michael@0 | 555 | const nsString &username, |
michael@0 | 556 | const nsString &password, |
michael@0 | 557 | const void *inBuf, |
michael@0 | 558 | uint32_t inLen, |
michael@0 | 559 | void **outBuf, |
michael@0 | 560 | uint32_t *outLen) |
michael@0 | 561 | { |
michael@0 | 562 | // inBuf contains Type-2 msg (the challenge) from server |
michael@0 | 563 | |
michael@0 | 564 | nsresult rv; |
michael@0 | 565 | Type2Msg msg; |
michael@0 | 566 | |
michael@0 | 567 | rv = ParseType2Msg(inBuf, inLen, &msg); |
michael@0 | 568 | if (NS_FAILED(rv)) |
michael@0 | 569 | return rv; |
michael@0 | 570 | |
michael@0 | 571 | bool unicode = (msg.flags & NTLM_NegotiateUnicode); |
michael@0 | 572 | |
michael@0 | 573 | // temporary buffers for unicode strings |
michael@0 | 574 | #ifdef IS_BIG_ENDIAN |
michael@0 | 575 | nsAutoString ucsDomainBuf, ucsUserBuf; |
michael@0 | 576 | #endif |
michael@0 | 577 | nsAutoString ucsHostBuf; |
michael@0 | 578 | // temporary buffers for oem strings |
michael@0 | 579 | nsAutoCString oemDomainBuf, oemUserBuf, oemHostBuf; |
michael@0 | 580 | // pointers and lengths for the string buffers; encoding is unicode if |
michael@0 | 581 | // the "negotiate unicode" flag was set in the Type-2 message. |
michael@0 | 582 | const void *domainPtr, *userPtr, *hostPtr; |
michael@0 | 583 | uint32_t domainLen, userLen, hostLen; |
michael@0 | 584 | |
michael@0 | 585 | // |
michael@0 | 586 | // get domain name |
michael@0 | 587 | // |
michael@0 | 588 | if (unicode) |
michael@0 | 589 | { |
michael@0 | 590 | #ifdef IS_BIG_ENDIAN |
michael@0 | 591 | ucsDomainBuf = domain; |
michael@0 | 592 | domainPtr = ucsDomainBuf.get(); |
michael@0 | 593 | domainLen = ucsDomainBuf.Length() * 2; |
michael@0 | 594 | WriteUnicodeLE((void *) domainPtr, (const char16_t *) domainPtr, |
michael@0 | 595 | ucsDomainBuf.Length()); |
michael@0 | 596 | #else |
michael@0 | 597 | domainPtr = domain.get(); |
michael@0 | 598 | domainLen = domain.Length() * 2; |
michael@0 | 599 | #endif |
michael@0 | 600 | } |
michael@0 | 601 | else |
michael@0 | 602 | { |
michael@0 | 603 | NS_CopyUnicodeToNative(domain, oemDomainBuf); |
michael@0 | 604 | domainPtr = oemDomainBuf.get(); |
michael@0 | 605 | domainLen = oemDomainBuf.Length(); |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | // |
michael@0 | 609 | // get user name |
michael@0 | 610 | // |
michael@0 | 611 | if (unicode) |
michael@0 | 612 | { |
michael@0 | 613 | #ifdef IS_BIG_ENDIAN |
michael@0 | 614 | ucsUserBuf = username; |
michael@0 | 615 | userPtr = ucsUserBuf.get(); |
michael@0 | 616 | userLen = ucsUserBuf.Length() * 2; |
michael@0 | 617 | WriteUnicodeLE((void *) userPtr, (const char16_t *) userPtr, |
michael@0 | 618 | ucsUserBuf.Length()); |
michael@0 | 619 | #else |
michael@0 | 620 | userPtr = username.get(); |
michael@0 | 621 | userLen = username.Length() * 2; |
michael@0 | 622 | #endif |
michael@0 | 623 | } |
michael@0 | 624 | else |
michael@0 | 625 | { |
michael@0 | 626 | NS_CopyUnicodeToNative(username, oemUserBuf); |
michael@0 | 627 | userPtr = oemUserBuf.get(); |
michael@0 | 628 | userLen = oemUserBuf.Length(); |
michael@0 | 629 | } |
michael@0 | 630 | |
michael@0 | 631 | // |
michael@0 | 632 | // get workstation name (use local machine's hostname) |
michael@0 | 633 | // |
michael@0 | 634 | char hostBuf[SYS_INFO_BUFFER_LENGTH]; |
michael@0 | 635 | if (PR_GetSystemInfo(PR_SI_HOSTNAME, hostBuf, sizeof(hostBuf)) == PR_FAILURE) |
michael@0 | 636 | return NS_ERROR_UNEXPECTED; |
michael@0 | 637 | hostLen = strlen(hostBuf); |
michael@0 | 638 | if (unicode) |
michael@0 | 639 | { |
michael@0 | 640 | // hostname is ASCII, so we can do a simple zero-pad expansion: |
michael@0 | 641 | CopyASCIItoUTF16(nsDependentCString(hostBuf, hostLen), ucsHostBuf); |
michael@0 | 642 | hostPtr = ucsHostBuf.get(); |
michael@0 | 643 | hostLen = ucsHostBuf.Length() * 2; |
michael@0 | 644 | #ifdef IS_BIG_ENDIAN |
michael@0 | 645 | WriteUnicodeLE((void *) hostPtr, (const char16_t *) hostPtr, |
michael@0 | 646 | ucsHostBuf.Length()); |
michael@0 | 647 | #endif |
michael@0 | 648 | } |
michael@0 | 649 | else |
michael@0 | 650 | hostPtr = hostBuf; |
michael@0 | 651 | |
michael@0 | 652 | // |
michael@0 | 653 | // now that we have generated all of the strings, we can allocate outBuf. |
michael@0 | 654 | // |
michael@0 | 655 | *outLen = NTLM_TYPE3_HEADER_LEN + hostLen + domainLen + userLen + |
michael@0 | 656 | LM_RESP_LEN + NTLM_RESP_LEN; |
michael@0 | 657 | *outBuf = nsMemory::Alloc(*outLen); |
michael@0 | 658 | if (!*outBuf) |
michael@0 | 659 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 660 | |
michael@0 | 661 | // |
michael@0 | 662 | // next, we compute the LM and NTLM responses. |
michael@0 | 663 | // |
michael@0 | 664 | uint8_t lmResp[LM_RESP_LEN], ntlmResp[NTLM_RESP_LEN], ntlmHash[NTLM_HASH_LEN]; |
michael@0 | 665 | if (msg.flags & NTLM_NegotiateNTLM2Key) |
michael@0 | 666 | { |
michael@0 | 667 | // compute NTLM2 session response |
michael@0 | 668 | uint8_t sessionHash[16], temp[16]; |
michael@0 | 669 | |
michael@0 | 670 | PK11_GenerateRandom(lmResp, 8); |
michael@0 | 671 | memset(lmResp + 8, 0, LM_RESP_LEN - 8); |
michael@0 | 672 | |
michael@0 | 673 | memcpy(temp, msg.challenge, 8); |
michael@0 | 674 | memcpy(temp + 8, lmResp, 8); |
michael@0 | 675 | md5sum(temp, 16, sessionHash); |
michael@0 | 676 | |
michael@0 | 677 | NTLM_Hash(password, ntlmHash); |
michael@0 | 678 | LM_Response(ntlmHash, sessionHash, ntlmResp); |
michael@0 | 679 | } |
michael@0 | 680 | else |
michael@0 | 681 | { |
michael@0 | 682 | NTLM_Hash(password, ntlmHash); |
michael@0 | 683 | LM_Response(ntlmHash, msg.challenge, ntlmResp); |
michael@0 | 684 | |
michael@0 | 685 | if (sendLM) |
michael@0 | 686 | { |
michael@0 | 687 | uint8_t lmHash[LM_HASH_LEN]; |
michael@0 | 688 | LM_Hash(password, lmHash); |
michael@0 | 689 | LM_Response(lmHash, msg.challenge, lmResp); |
michael@0 | 690 | } |
michael@0 | 691 | else |
michael@0 | 692 | { |
michael@0 | 693 | // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2, |
michael@0 | 694 | // the correct way to not send the LM hash is to send the NTLM hash twice |
michael@0 | 695 | // in both the LM and NTLM response fields. |
michael@0 | 696 | LM_Response(ntlmHash, msg.challenge, lmResp); |
michael@0 | 697 | } |
michael@0 | 698 | } |
michael@0 | 699 | |
michael@0 | 700 | // |
michael@0 | 701 | // finally, we assemble the Type-3 msg :-) |
michael@0 | 702 | // |
michael@0 | 703 | void *cursor = *outBuf; |
michael@0 | 704 | uint32_t offset; |
michael@0 | 705 | |
michael@0 | 706 | // 0 : signature |
michael@0 | 707 | cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); |
michael@0 | 708 | |
michael@0 | 709 | // 8 : marker |
michael@0 | 710 | cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER)); |
michael@0 | 711 | |
michael@0 | 712 | // 12 : LM response sec buf |
michael@0 | 713 | offset = NTLM_TYPE3_HEADER_LEN + domainLen + userLen + hostLen; |
michael@0 | 714 | cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset); |
michael@0 | 715 | memcpy((uint8_t *) *outBuf + offset, lmResp, LM_RESP_LEN); |
michael@0 | 716 | |
michael@0 | 717 | // 20 : NTLM response sec buf |
michael@0 | 718 | offset += LM_RESP_LEN; |
michael@0 | 719 | cursor = WriteSecBuf(cursor, NTLM_RESP_LEN, offset); |
michael@0 | 720 | memcpy((uint8_t *) *outBuf + offset, ntlmResp, NTLM_RESP_LEN); |
michael@0 | 721 | |
michael@0 | 722 | // 28 : domain name sec buf |
michael@0 | 723 | offset = NTLM_TYPE3_HEADER_LEN; |
michael@0 | 724 | cursor = WriteSecBuf(cursor, domainLen, offset); |
michael@0 | 725 | memcpy((uint8_t *) *outBuf + offset, domainPtr, domainLen); |
michael@0 | 726 | |
michael@0 | 727 | // 36 : user name sec buf |
michael@0 | 728 | offset += domainLen; |
michael@0 | 729 | cursor = WriteSecBuf(cursor, userLen, offset); |
michael@0 | 730 | memcpy((uint8_t *) *outBuf + offset, userPtr, userLen); |
michael@0 | 731 | |
michael@0 | 732 | // 44 : workstation (host) name sec buf |
michael@0 | 733 | offset += userLen; |
michael@0 | 734 | cursor = WriteSecBuf(cursor, hostLen, offset); |
michael@0 | 735 | memcpy((uint8_t *) *outBuf + offset, hostPtr, hostLen); |
michael@0 | 736 | |
michael@0 | 737 | // 52 : session key sec buf (not used) |
michael@0 | 738 | cursor = WriteSecBuf(cursor, 0, 0); |
michael@0 | 739 | |
michael@0 | 740 | // 60 : negotiated flags |
michael@0 | 741 | cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS); |
michael@0 | 742 | |
michael@0 | 743 | return NS_OK; |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | //----------------------------------------------------------------------------- |
michael@0 | 747 | |
michael@0 | 748 | NS_IMPL_ISUPPORTS(nsNTLMAuthModule, nsIAuthModule) |
michael@0 | 749 | |
michael@0 | 750 | nsNTLMAuthModule::~nsNTLMAuthModule() |
michael@0 | 751 | { |
michael@0 | 752 | ZapString(mPassword); |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | nsresult |
michael@0 | 756 | nsNTLMAuthModule::InitTest() |
michael@0 | 757 | { |
michael@0 | 758 | nsNSSShutDownPreventionLock locker; |
michael@0 | 759 | // |
michael@0 | 760 | // disable NTLM authentication when FIPS mode is enabled. |
michael@0 | 761 | // |
michael@0 | 762 | return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK; |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | NS_IMETHODIMP |
michael@0 | 766 | nsNTLMAuthModule::Init(const char *serviceName, |
michael@0 | 767 | uint32_t serviceFlags, |
michael@0 | 768 | const char16_t *domain, |
michael@0 | 769 | const char16_t *username, |
michael@0 | 770 | const char16_t *password) |
michael@0 | 771 | { |
michael@0 | 772 | NS_ASSERTION((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) == nsIAuthModule::REQ_DEFAULT, |
michael@0 | 773 | "unexpected service flags"); |
michael@0 | 774 | |
michael@0 | 775 | mDomain = domain; |
michael@0 | 776 | mUsername = username; |
michael@0 | 777 | mPassword = password; |
michael@0 | 778 | |
michael@0 | 779 | static bool sTelemetrySent = false; |
michael@0 | 780 | if (!sTelemetrySent) { |
michael@0 | 781 | mozilla::Telemetry::Accumulate( |
michael@0 | 782 | mozilla::Telemetry::NTLM_MODULE_USED_2, |
michael@0 | 783 | serviceFlags & nsIAuthModule::REQ_PROXY_AUTH |
michael@0 | 784 | ? NTLM_MODULE_GENERIC_PROXY |
michael@0 | 785 | : NTLM_MODULE_GENERIC_DIRECT); |
michael@0 | 786 | sTelemetrySent = true; |
michael@0 | 787 | } |
michael@0 | 788 | |
michael@0 | 789 | return NS_OK; |
michael@0 | 790 | } |
michael@0 | 791 | |
michael@0 | 792 | NS_IMETHODIMP |
michael@0 | 793 | nsNTLMAuthModule::GetNextToken(const void *inToken, |
michael@0 | 794 | uint32_t inTokenLen, |
michael@0 | 795 | void **outToken, |
michael@0 | 796 | uint32_t *outTokenLen) |
michael@0 | 797 | { |
michael@0 | 798 | nsresult rv; |
michael@0 | 799 | nsNSSShutDownPreventionLock locker; |
michael@0 | 800 | // |
michael@0 | 801 | // disable NTLM authentication when FIPS mode is enabled. |
michael@0 | 802 | // |
michael@0 | 803 | if (PK11_IsFIPS()) |
michael@0 | 804 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 805 | |
michael@0 | 806 | // if inToken is non-null, then assume it contains a type 2 message... |
michael@0 | 807 | if (inToken) |
michael@0 | 808 | { |
michael@0 | 809 | LogToken("in-token", inToken, inTokenLen); |
michael@0 | 810 | rv = GenerateType3Msg(mDomain, mUsername, mPassword, inToken, |
michael@0 | 811 | inTokenLen, outToken, outTokenLen); |
michael@0 | 812 | } |
michael@0 | 813 | else |
michael@0 | 814 | { |
michael@0 | 815 | rv = GenerateType1Msg(outToken, outTokenLen); |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | #ifdef PR_LOGGING |
michael@0 | 819 | if (NS_SUCCEEDED(rv)) |
michael@0 | 820 | LogToken("out-token", *outToken, *outTokenLen); |
michael@0 | 821 | #endif |
michael@0 | 822 | |
michael@0 | 823 | return rv; |
michael@0 | 824 | } |
michael@0 | 825 | |
michael@0 | 826 | NS_IMETHODIMP |
michael@0 | 827 | nsNTLMAuthModule::Unwrap(const void *inToken, |
michael@0 | 828 | uint32_t inTokenLen, |
michael@0 | 829 | void **outToken, |
michael@0 | 830 | uint32_t *outTokenLen) |
michael@0 | 831 | { |
michael@0 | 832 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 833 | } |
michael@0 | 834 | |
michael@0 | 835 | NS_IMETHODIMP |
michael@0 | 836 | nsNTLMAuthModule::Wrap(const void *inToken, |
michael@0 | 837 | uint32_t inTokenLen, |
michael@0 | 838 | bool confidential, |
michael@0 | 839 | void **outToken, |
michael@0 | 840 | uint32_t *outTokenLen) |
michael@0 | 841 | { |
michael@0 | 842 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | //----------------------------------------------------------------------------- |
michael@0 | 846 | // DES support code |
michael@0 | 847 | |
michael@0 | 848 | // set odd parity bit (in least significant bit position) |
michael@0 | 849 | static uint8_t |
michael@0 | 850 | des_setkeyparity(uint8_t x) |
michael@0 | 851 | { |
michael@0 | 852 | if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^ |
michael@0 | 853 | (x >> 4) ^ (x >> 3) ^ (x >> 2) ^ |
michael@0 | 854 | (x >> 1)) & 0x01) == 0) |
michael@0 | 855 | x |= 0x01; |
michael@0 | 856 | else |
michael@0 | 857 | x &= 0xfe; |
michael@0 | 858 | return x; |
michael@0 | 859 | } |
michael@0 | 860 | |
michael@0 | 861 | // build 64-bit des key from 56-bit raw key |
michael@0 | 862 | static void |
michael@0 | 863 | des_makekey(const uint8_t *raw, uint8_t *key) |
michael@0 | 864 | { |
michael@0 | 865 | key[0] = des_setkeyparity(raw[0]); |
michael@0 | 866 | key[1] = des_setkeyparity((raw[0] << 7) | (raw[1] >> 1)); |
michael@0 | 867 | key[2] = des_setkeyparity((raw[1] << 6) | (raw[2] >> 2)); |
michael@0 | 868 | key[3] = des_setkeyparity((raw[2] << 5) | (raw[3] >> 3)); |
michael@0 | 869 | key[4] = des_setkeyparity((raw[3] << 4) | (raw[4] >> 4)); |
michael@0 | 870 | key[5] = des_setkeyparity((raw[4] << 3) | (raw[5] >> 5)); |
michael@0 | 871 | key[6] = des_setkeyparity((raw[5] << 2) | (raw[6] >> 6)); |
michael@0 | 872 | key[7] = des_setkeyparity((raw[6] << 1)); |
michael@0 | 873 | } |
michael@0 | 874 | |
michael@0 | 875 | // run des encryption algorithm (using NSS) |
michael@0 | 876 | static void |
michael@0 | 877 | des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash) |
michael@0 | 878 | { |
michael@0 | 879 | CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB; |
michael@0 | 880 | PK11SlotInfo *slot = nullptr; |
michael@0 | 881 | PK11SymKey *symkey = nullptr; |
michael@0 | 882 | PK11Context *ctxt = nullptr; |
michael@0 | 883 | SECItem keyItem, *param = nullptr; |
michael@0 | 884 | SECStatus rv; |
michael@0 | 885 | unsigned int n; |
michael@0 | 886 | |
michael@0 | 887 | slot = PK11_GetBestSlot(cipherMech, nullptr); |
michael@0 | 888 | if (!slot) |
michael@0 | 889 | { |
michael@0 | 890 | NS_ERROR("no slot"); |
michael@0 | 891 | goto done; |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | keyItem.data = (uint8_t *) key; |
michael@0 | 895 | keyItem.len = 8; |
michael@0 | 896 | symkey = PK11_ImportSymKey(slot, cipherMech, |
michael@0 | 897 | PK11_OriginUnwrap, CKA_ENCRYPT, |
michael@0 | 898 | &keyItem, nullptr); |
michael@0 | 899 | if (!symkey) |
michael@0 | 900 | { |
michael@0 | 901 | NS_ERROR("no symkey"); |
michael@0 | 902 | goto done; |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | // no initialization vector required |
michael@0 | 906 | param = PK11_ParamFromIV(cipherMech, nullptr); |
michael@0 | 907 | if (!param) |
michael@0 | 908 | { |
michael@0 | 909 | NS_ERROR("no param"); |
michael@0 | 910 | goto done; |
michael@0 | 911 | } |
michael@0 | 912 | |
michael@0 | 913 | ctxt = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, |
michael@0 | 914 | symkey, param); |
michael@0 | 915 | if (!ctxt) |
michael@0 | 916 | { |
michael@0 | 917 | NS_ERROR("no context"); |
michael@0 | 918 | goto done; |
michael@0 | 919 | } |
michael@0 | 920 | |
michael@0 | 921 | rv = PK11_CipherOp(ctxt, hash, (int *) &n, 8, (uint8_t *) src, 8); |
michael@0 | 922 | if (rv != SECSuccess) |
michael@0 | 923 | { |
michael@0 | 924 | NS_ERROR("des failure"); |
michael@0 | 925 | goto done; |
michael@0 | 926 | } |
michael@0 | 927 | |
michael@0 | 928 | rv = PK11_DigestFinal(ctxt, hash+8, &n, 0); |
michael@0 | 929 | if (rv != SECSuccess) |
michael@0 | 930 | { |
michael@0 | 931 | NS_ERROR("des failure"); |
michael@0 | 932 | goto done; |
michael@0 | 933 | } |
michael@0 | 934 | |
michael@0 | 935 | done: |
michael@0 | 936 | if (ctxt) |
michael@0 | 937 | PK11_DestroyContext(ctxt, true); |
michael@0 | 938 | if (symkey) |
michael@0 | 939 | PK11_FreeSymKey(symkey); |
michael@0 | 940 | if (param) |
michael@0 | 941 | SECITEM_FreeItem(param, true); |
michael@0 | 942 | if (slot) |
michael@0 | 943 | PK11_FreeSlot(slot); |
michael@0 | 944 | } |
michael@0 | 945 | |
michael@0 | 946 | //----------------------------------------------------------------------------- |
michael@0 | 947 | // MD5 support code |
michael@0 | 948 | |
michael@0 | 949 | static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result) |
michael@0 | 950 | { |
michael@0 | 951 | PK11Context *ctxt = PK11_CreateDigestContext(SEC_OID_MD5); |
michael@0 | 952 | if (ctxt) |
michael@0 | 953 | { |
michael@0 | 954 | if (PK11_DigestBegin(ctxt) == SECSuccess) |
michael@0 | 955 | { |
michael@0 | 956 | if (PK11_DigestOp(ctxt, input, inputLen) == SECSuccess) |
michael@0 | 957 | { |
michael@0 | 958 | uint32_t resultLen = 16; |
michael@0 | 959 | PK11_DigestFinal(ctxt, result, &resultLen, resultLen); |
michael@0 | 960 | } |
michael@0 | 961 | } |
michael@0 | 962 | PK11_DestroyContext(ctxt, true); |
michael@0 | 963 | } |
michael@0 | 964 | } |