1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "net/http/http_auth_handler_ntlm.h" 6 7#include <stdlib.h> 8// For gethostname 9#if defined(OS_POSIX) 10#include <unistd.h> 11#elif defined(OS_WIN) 12#include <winsock2.h> 13#endif 14 15#include "base/md5.h" 16#include "base/rand_util.h" 17#include "base/string_util.h" 18#include "base/sys_string_conversions.h" 19#include "base/utf_string_conversions.h" 20#include "net/base/net_errors.h" 21#include "net/base/net_util.h" 22#include "net/http/des.h" 23#include "net/http/md4.h" 24 25namespace net { 26 27// Based on mozilla/security/manager/ssl/src/nsNTLMAuthModule.cpp, 28// CVS rev. 1.14. 29// 30// TODO(wtc): 31// - The IS_BIG_ENDIAN code is not tested. 32// - Enable the logging code or just delete it. 33// - Delete or comment out the LM code, which hasn't been tested and isn't 34// being used. 35 36/* ***** BEGIN LICENSE BLOCK ***** 37 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 38 * 39 * The contents of this file are subject to the Mozilla Public License Version 40 * 1.1 (the "License"); you may not use this file except in compliance with 41 * the License. You may obtain a copy of the License at 42 * http://www.mozilla.org/MPL/ 43 * 44 * Software distributed under the License is distributed on an "AS IS" basis, 45 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 46 * for the specific language governing rights and limitations under the 47 * License. 48 * 49 * The Original Code is Mozilla. 50 * 51 * The Initial Developer of the Original Code is IBM Corporation. 52 * Portions created by IBM Corporation are Copyright (C) 2003 53 * IBM Corporation. All Rights Reserved. 54 * 55 * Contributor(s): 56 * Darin Fisher <darin@meer.net> 57 * 58 * Alternatively, the contents of this file may be used under the terms of 59 * either the GNU General Public License Version 2 or later (the "GPL"), or 60 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 61 * in which case the provisions of the GPL or the LGPL are applicable instead 62 * of those above. If you wish to allow use of your version of this file only 63 * under the terms of either the GPL or the LGPL, and not to allow others to 64 * use your version of this file under the terms of the MPL, indicate your 65 * decision by deleting the provisions above and replace them with the notice 66 * and other provisions required by the GPL or the LGPL. If you do not delete 67 * the provisions above, a recipient may use your version of this file under 68 * the terms of any one of the MPL, the GPL or the LGPL. 69 * 70 * ***** END LICENSE BLOCK ***** */ 71 72// Discover the endianness by testing processor architecture. 73#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_X86_64)\ 74 || defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPSEL) 75#define IS_LITTLE_ENDIAN 1 76#undef IS_BIG_ENDIAN 77#elif defined(ARCH_CPU_MIPSEB) 78#undef IS_LITTLE_ENDIAN 79#define IS_BIG_ENDIAN 1 80#else 81#error "Unknown endianness" 82#endif 83 84#define NTLM_LOG(x) ((void) 0) 85 86//----------------------------------------------------------------------------- 87// This file contains a cross-platform NTLM authentication implementation. It 88// is based on documentation from: http://davenport.sourceforge.net/ntlm.html 89//----------------------------------------------------------------------------- 90 91enum { 92 NTLM_NegotiateUnicode = 0x00000001, 93 NTLM_NegotiateOEM = 0x00000002, 94 NTLM_RequestTarget = 0x00000004, 95 NTLM_Unknown1 = 0x00000008, 96 NTLM_NegotiateSign = 0x00000010, 97 NTLM_NegotiateSeal = 0x00000020, 98 NTLM_NegotiateDatagramStyle = 0x00000040, 99 NTLM_NegotiateLanManagerKey = 0x00000080, 100 NTLM_NegotiateNetware = 0x00000100, 101 NTLM_NegotiateNTLMKey = 0x00000200, 102 NTLM_Unknown2 = 0x00000400, 103 NTLM_Unknown3 = 0x00000800, 104 NTLM_NegotiateDomainSupplied = 0x00001000, 105 NTLM_NegotiateWorkstationSupplied = 0x00002000, 106 NTLM_NegotiateLocalCall = 0x00004000, 107 NTLM_NegotiateAlwaysSign = 0x00008000, 108 NTLM_TargetTypeDomain = 0x00010000, 109 NTLM_TargetTypeServer = 0x00020000, 110 NTLM_TargetTypeShare = 0x00040000, 111 NTLM_NegotiateNTLM2Key = 0x00080000, 112 NTLM_RequestInitResponse = 0x00100000, 113 NTLM_RequestAcceptResponse = 0x00200000, 114 NTLM_RequestNonNTSessionKey = 0x00400000, 115 NTLM_NegotiateTargetInfo = 0x00800000, 116 NTLM_Unknown4 = 0x01000000, 117 NTLM_Unknown5 = 0x02000000, 118 NTLM_Unknown6 = 0x04000000, 119 NTLM_Unknown7 = 0x08000000, 120 NTLM_Unknown8 = 0x10000000, 121 NTLM_Negotiate128 = 0x20000000, 122 NTLM_NegotiateKeyExchange = 0x40000000, 123 NTLM_Negotiate56 = 0x80000000 124}; 125 126// We send these flags with our type 1 message. 127enum { 128 NTLM_TYPE1_FLAGS = (NTLM_NegotiateUnicode | 129 NTLM_NegotiateOEM | 130 NTLM_RequestTarget | 131 NTLM_NegotiateNTLMKey | 132 NTLM_NegotiateAlwaysSign | 133 NTLM_NegotiateNTLM2Key) 134}; 135 136static const char NTLM_SIGNATURE[] = "NTLMSSP"; 137static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 }; 138static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 }; 139static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 }; 140 141enum { 142 NTLM_TYPE1_HEADER_LEN = 32, 143 NTLM_TYPE2_HEADER_LEN = 32, 144 NTLM_TYPE3_HEADER_LEN = 64, 145 146 LM_HASH_LEN = 16, 147 LM_RESP_LEN = 24, 148 149 NTLM_HASH_LEN = 16, 150 NTLM_RESP_LEN = 24 151}; 152 153//----------------------------------------------------------------------------- 154 155// The return value of this function controls whether or not the LM hash will 156// be included in response to a NTLM challenge. 157// 158// In Mozilla, this function returns the value of the boolean preference 159// "network.ntlm.send-lm-response". By default, the preference is disabled 160// since servers should almost never need the LM hash, and the LM hash is what 161// makes NTLM authentication less secure. See 162// https://bugzilla.mozilla.org/show_bug.cgi?id=250691 for further details. 163// 164// We just return a hardcoded false. 165static bool SendLM() { 166 return false; 167} 168 169//----------------------------------------------------------------------------- 170 171#define LogFlags(x) ((void) 0) 172#define LogBuf(a, b, c) ((void) 0) 173#define LogToken(a, b, c) ((void) 0) 174 175//----------------------------------------------------------------------------- 176 177// Byte order swapping. 178#define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) 179#define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16))) 180 181static void* WriteBytes(void* buf, const void* data, uint32 data_len) { 182 memcpy(buf, data, data_len); 183 return static_cast<char*>(buf) + data_len; 184} 185 186static void* WriteDWORD(void* buf, uint32 dword) { 187#ifdef IS_BIG_ENDIAN 188 // NTLM uses little endian on the wire. 189 dword = SWAP32(dword); 190#endif 191 return WriteBytes(buf, &dword, sizeof(dword)); 192} 193 194static void* WriteSecBuf(void* buf, uint16 length, uint32 offset) { 195#ifdef IS_BIG_ENDIAN 196 length = SWAP16(length); 197 offset = SWAP32(offset); 198#endif 199 buf = WriteBytes(buf, &length, sizeof(length)); 200 buf = WriteBytes(buf, &length, sizeof(length)); 201 buf = WriteBytes(buf, &offset, sizeof(offset)); 202 return buf; 203} 204 205#ifdef IS_BIG_ENDIAN 206/** 207 * WriteUnicodeLE copies a unicode string from one buffer to another. The 208 * resulting unicode string is in little-endian format. The input string is 209 * assumed to be in the native endianness of the local machine. It is safe 210 * to pass the same buffer as both input and output, which is a handy way to 211 * convert the unicode buffer to little-endian on big-endian platforms. 212 */ 213static void* WriteUnicodeLE(void* buf, const char16* str, uint32 str_len) { 214 // Convert input string from BE to LE. 215 uint8* cursor = static_cast<uint8*>(buf); 216 const uint8* input = reinterpret_cast<const uint8*>(str); 217 for (uint32 i = 0; i < str_len; ++i, input += 2, cursor += 2) { 218 // Allow for the case where |buf == str|. 219 uint8 temp = input[0]; 220 cursor[0] = input[1]; 221 cursor[1] = temp; 222 } 223 return buf; 224} 225#endif 226 227static uint16 ReadUint16(const uint8*& buf) { 228 uint16 x = (static_cast<uint16>(buf[0])) | 229 (static_cast<uint16>(buf[1]) << 8); 230 buf += sizeof(x); 231 return x; 232} 233 234static uint32 ReadUint32(const uint8*& buf) { 235 uint32 x = (static_cast<uint32>(buf[0])) | 236 (static_cast<uint32>(buf[1]) << 8) | 237 (static_cast<uint32>(buf[2]) << 16) | 238 (static_cast<uint32>(buf[3]) << 24); 239 buf += sizeof(x); 240 return x; 241} 242 243//----------------------------------------------------------------------------- 244 245static void ZapBuf(void* buf, size_t buf_len) { 246 memset(buf, 0, buf_len); 247} 248 249// TODO(wtc): Can we implement ZapString as 250// s.replace(0, s.size(), s.size(), '\0)? 251static void ZapString(std::string* s) { 252 ZapBuf(&(*s)[0], s->length()); 253} 254 255static void ZapString(string16* s) { 256 ZapBuf(&(*s)[0], s->length() * 2); 257} 258 259// LM_Hash computes the LM hash of the given password. 260// 261// param password 262// unicode password. 263// param hash 264// 16-byte result buffer 265// 266// Note: This function is not being used because our SendLM() function always 267// returns false. 268static void LM_Hash(const string16& password, uint8* hash) { 269 static const uint8 LM_MAGIC[] = "KGS!@#$%"; 270 271 // Convert password to OEM character set. We'll just use the native 272 // filesystem charset. 273 std::string passbuf = base::SysWideToNativeMB(UTF16ToWide(password)); 274 StringToUpperASCII(&passbuf); 275 passbuf.resize(14, '\0'); 276 277 uint8 k1[8], k2[8]; 278 DESMakeKey(reinterpret_cast<const uint8*>(passbuf.data()) , k1); 279 DESMakeKey(reinterpret_cast<const uint8*>(passbuf.data()) + 7, k2); 280 ZapString(&passbuf); 281 282 // Use password keys to hash LM magic string twice. 283 DESEncrypt(k1, LM_MAGIC, hash); 284 DESEncrypt(k2, LM_MAGIC, hash + 8); 285} 286 287// NTLM_Hash computes the NTLM hash of the given password. 288// 289// param password 290// null-terminated unicode password. 291// param hash 292// 16-byte result buffer 293static void NTLM_Hash(const string16& password, uint8* hash) { 294#ifdef IS_BIG_ENDIAN 295 uint32 len = password.length(); 296 uint8* passbuf; 297 298 passbuf = static_cast<uint8*>(malloc(len * 2)); 299 WriteUnicodeLE(passbuf, password.data(), len); 300 weak_crypto::MD4Sum(passbuf, len * 2, hash); 301 302 ZapBuf(passbuf, len * 2); 303 free(passbuf); 304#else 305 weak_crypto::MD4Sum(reinterpret_cast<const uint8*>(password.data()), 306 password.length() * 2, hash); 307#endif 308} 309 310//----------------------------------------------------------------------------- 311 312// LM_Response generates the LM response given a 16-byte password hash and the 313// challenge from the Type-2 message. 314// 315// param hash 316// 16-byte password hash 317// param challenge 318// 8-byte challenge from Type-2 message 319// param response 320// 24-byte buffer to contain the LM response upon return 321static void LM_Response(const uint8* hash, 322 const uint8* challenge, 323 uint8* response) { 324 uint8 keybytes[21], k1[8], k2[8], k3[8]; 325 326 memcpy(keybytes, hash, 16); 327 ZapBuf(keybytes + 16, 5); 328 329 DESMakeKey(keybytes , k1); 330 DESMakeKey(keybytes + 7, k2); 331 DESMakeKey(keybytes + 14, k3); 332 333 DESEncrypt(k1, challenge, response); 334 DESEncrypt(k2, challenge, response + 8); 335 DESEncrypt(k3, challenge, response + 16); 336} 337 338//----------------------------------------------------------------------------- 339 340// Returns OK or a network error code. 341static int GenerateType1Msg(void** out_buf, uint32* out_len) { 342 // 343 // Verify that buf_len is sufficient. 344 // 345 *out_len = NTLM_TYPE1_HEADER_LEN; 346 *out_buf = malloc(*out_len); 347 if (!*out_buf) 348 return ERR_OUT_OF_MEMORY; 349 350 // 351 // Write out type 1 message. 352 // 353 void* cursor = *out_buf; 354 355 // 0 : signature 356 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); 357 358 // 8 : marker 359 cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER)); 360 361 // 12 : flags 362 cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS); 363 364 // 365 // NOTE: It is common for the domain and workstation fields to be empty. 366 // This is true of Win2k clients, and my guess is that there is 367 // little utility to sending these strings before the charset has 368 // been negotiated. We follow suite -- anyways, it doesn't hurt 369 // to save some bytes on the wire ;-) 370 // 371 372 // 16 : supplied domain security buffer (empty) 373 cursor = WriteSecBuf(cursor, 0, 0); 374 375 // 24 : supplied workstation security buffer (empty) 376 cursor = WriteSecBuf(cursor, 0, 0); 377 378 return OK; 379} 380 381struct Type2Msg { 382 uint32 flags; // NTLM_Xxx bitwise combination 383 uint8 challenge[8]; // 8 byte challenge 384 const void* target; // target string (type depends on flags) 385 uint32 target_len; // target length in bytes 386}; 387 388// Returns OK or a network error code. 389// TODO(wtc): This function returns ERR_UNEXPECTED when the input message is 390// invalid. We should return a better error code. 391static int ParseType2Msg(const void* in_buf, uint32 in_len, Type2Msg* msg) { 392 // Make sure in_buf is long enough to contain a meaningful type2 msg. 393 // 394 // 0 NTLMSSP Signature 395 // 8 NTLM Message Type 396 // 12 Target Name 397 // 20 Flags 398 // 24 Challenge 399 // 32 end of header, start of optional data blocks 400 // 401 if (in_len < NTLM_TYPE2_HEADER_LEN) 402 return ERR_UNEXPECTED; 403 404 const uint8* cursor = (const uint8*) in_buf; 405 406 // verify NTLMSSP signature 407 if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0) 408 return ERR_UNEXPECTED; 409 cursor += sizeof(NTLM_SIGNATURE); 410 411 // verify Type-2 marker 412 if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0) 413 return ERR_UNEXPECTED; 414 cursor += sizeof(NTLM_TYPE2_MARKER); 415 416 // read target name security buffer 417 uint32 target_len = ReadUint16(cursor); 418 ReadUint16(cursor); // discard next 16-bit value 419 uint32 offset = ReadUint32(cursor); // get offset from in_buf 420 msg->target_len = 0; 421 msg->target = NULL; 422 // Check the offset / length combo is in range of the input buffer, including 423 // integer overflow checking. 424 if (offset + target_len > offset && offset + target_len <= in_len) { 425 msg->target_len = target_len; 426 msg->target = ((const uint8*) in_buf) + offset; 427 } 428 429 // read flags 430 msg->flags = ReadUint32(cursor); 431 432 // read challenge 433 memcpy(msg->challenge, cursor, sizeof(msg->challenge)); 434 cursor += sizeof(msg->challenge); 435 436 NTLM_LOG(("NTLM type 2 message:\n")); 437 LogBuf("target", (const uint8*) msg->target, msg->target_len); 438 LogBuf("flags", (const uint8*) &msg->flags, 4); 439 LogFlags(msg->flags); 440 LogBuf("challenge", msg->challenge, sizeof(msg->challenge)); 441 442 // We currently do not implement LMv2/NTLMv2 or NTLM2 responses, 443 // so we can ignore target information. We may want to enable 444 // support for these alternate mechanisms in the future. 445 return OK; 446} 447 448static void GenerateRandom(uint8* output, size_t n) { 449 for (size_t i = 0; i < n; ++i) 450 output[i] = base::RandInt(0, 255); 451} 452 453// Returns OK or a network error code. 454static int GenerateType3Msg(const string16& domain, 455 const string16& username, 456 const string16& password, 457 const std::string& hostname, 458 const void* rand_8_bytes, 459 const void* in_buf, 460 uint32 in_len, 461 void** out_buf, 462 uint32* out_len) { 463 // in_buf contains Type-2 msg (the challenge) from server. 464 465 int rv; 466 Type2Msg msg; 467 468 rv = ParseType2Msg(in_buf, in_len, &msg); 469 if (rv != OK) 470 return rv; 471 472 bool unicode = (msg.flags & NTLM_NegotiateUnicode) != 0; 473 474 // Temporary buffers for unicode strings 475#ifdef IS_BIG_ENDIAN 476 string16 ucs_domain_buf, ucs_user_buf; 477#endif 478 string16 ucs_host_buf; 479 // Temporary buffers for oem strings 480 std::string oem_domain_buf, oem_user_buf; 481 // Pointers and lengths for the string buffers; encoding is unicode if 482 // the "negotiate unicode" flag was set in the Type-2 message. 483 const void* domain_ptr; 484 const void* user_ptr; 485 const void* host_ptr; 486 uint32 domain_len, user_len, host_len; 487 488 // 489 // Get domain name. 490 // 491 if (unicode) { 492#ifdef IS_BIG_ENDIAN 493 ucs_domain_buf = domain; 494 domain_ptr = ucs_domain_buf.data(); 495 domain_len = ucs_domain_buf.length() * 2; 496 WriteUnicodeLE(const_cast<void*>(domain_ptr), (const char16*) domain_ptr, 497 ucs_domain_buf.length()); 498#else 499 domain_ptr = domain.data(); 500 domain_len = domain.length() * 2; 501#endif 502 } else { 503 oem_domain_buf = base::SysWideToNativeMB(UTF16ToWide(domain)); 504 domain_ptr = oem_domain_buf.data(); 505 domain_len = oem_domain_buf.length(); 506 } 507 508 // 509 // Get user name. 510 // 511 if (unicode) { 512#ifdef IS_BIG_ENDIAN 513 ucs_user_buf = username; 514 user_ptr = ucs_user_buf.data(); 515 user_len = ucs_user_buf.length() * 2; 516 WriteUnicodeLE(const_cast<void*>(user_ptr), (const char16*) user_ptr, 517 ucs_user_buf.length()); 518#else 519 user_ptr = username.data(); 520 user_len = username.length() * 2; 521#endif 522 } else { 523 oem_user_buf = base::SysWideToNativeMB(UTF16ToWide(username)); 524 user_ptr = oem_user_buf.data(); 525 user_len = oem_user_buf.length(); 526 } 527 528 // 529 // Get workstation name (use local machine's hostname). 530 // 531 if (unicode) { 532 // hostname is ASCII, so we can do a simple zero-pad expansion: 533 ucs_host_buf.assign(hostname.begin(), hostname.end()); 534 host_ptr = ucs_host_buf.data(); 535 host_len = ucs_host_buf.length() * 2; 536#ifdef IS_BIG_ENDIAN 537 WriteUnicodeLE(const_cast<void*>(host_ptr), (const char16*) host_ptr, 538 ucs_host_buf.length()); 539#endif 540 } else { 541 host_ptr = hostname.data(); 542 host_len = hostname.length(); 543 } 544 545 // 546 // Now that we have generated all of the strings, we can allocate out_buf. 547 // 548 *out_len = NTLM_TYPE3_HEADER_LEN + host_len + domain_len + user_len + 549 LM_RESP_LEN + NTLM_RESP_LEN; 550 *out_buf = malloc(*out_len); 551 if (!*out_buf) 552 return ERR_OUT_OF_MEMORY; 553 554 // 555 // Next, we compute the LM and NTLM responses. 556 // 557 uint8 lm_resp[LM_RESP_LEN]; 558 uint8 ntlm_resp[NTLM_RESP_LEN]; 559 uint8 ntlm_hash[NTLM_HASH_LEN]; 560 if (msg.flags & NTLM_NegotiateNTLM2Key) { 561 // compute NTLM2 session response 562 MD5Digest session_hash; 563 uint8 temp[16]; 564 565 memcpy(lm_resp, rand_8_bytes, 8); 566 memset(lm_resp + 8, 0, LM_RESP_LEN - 8); 567 568 memcpy(temp, msg.challenge, 8); 569 memcpy(temp + 8, lm_resp, 8); 570 MD5Sum(temp, 16, &session_hash); 571 572 NTLM_Hash(password, ntlm_hash); 573 LM_Response(ntlm_hash, session_hash.a, ntlm_resp); 574 } else { 575 NTLM_Hash(password, ntlm_hash); 576 LM_Response(ntlm_hash, msg.challenge, ntlm_resp); 577 578 if (SendLM()) { 579 uint8 lm_hash[LM_HASH_LEN]; 580 LM_Hash(password, lm_hash); 581 LM_Response(lm_hash, msg.challenge, lm_resp); 582 } else { 583 // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2, 584 // the correct way to not send the LM hash is to send the NTLM hash twice 585 // in both the LM and NTLM response fields. 586 LM_Response(ntlm_hash, msg.challenge, lm_resp); 587 } 588 } 589 590 // 591 // Finally, we assemble the Type-3 msg :-) 592 // 593 void* cursor = *out_buf; 594 uint32 offset; 595 596 // 0 : signature 597 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); 598 599 // 8 : marker 600 cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER)); 601 602 // 12 : LM response sec buf 603 offset = NTLM_TYPE3_HEADER_LEN + domain_len + user_len + host_len; 604 cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset); 605 memcpy(static_cast<uint8*>(*out_buf) + offset, lm_resp, LM_RESP_LEN); 606 607 // 20 : NTLM response sec buf 608 offset += LM_RESP_LEN; 609 cursor = WriteSecBuf(cursor, NTLM_RESP_LEN, offset); 610 memcpy(static_cast<uint8*>(*out_buf) + offset, ntlm_resp, NTLM_RESP_LEN); 611 612 // 28 : domain name sec buf 613 offset = NTLM_TYPE3_HEADER_LEN; 614 cursor = WriteSecBuf(cursor, domain_len, offset); 615 memcpy(static_cast<uint8*>(*out_buf) + offset, domain_ptr, domain_len); 616 617 // 36 : user name sec buf 618 offset += domain_len; 619 cursor = WriteSecBuf(cursor, user_len, offset); 620 memcpy(static_cast<uint8*>(*out_buf) + offset, user_ptr, user_len); 621 622 // 44 : workstation (host) name sec buf 623 offset += user_len; 624 cursor = WriteSecBuf(cursor, host_len, offset); 625 memcpy(static_cast<uint8*>(*out_buf) + offset, host_ptr, host_len); 626 627 // 52 : session key sec buf (not used) 628 cursor = WriteSecBuf(cursor, 0, 0); 629 630 // 60 : negotiated flags 631 cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS); 632 633 return OK; 634} 635 636// NTLM authentication is specified in "NTLM Over HTTP Protocol Specification" 637// [MS-NTHT]. 638 639// static 640HttpAuthHandlerNTLM::GenerateRandomProc 641HttpAuthHandlerNTLM::generate_random_proc_ = GenerateRandom; 642 643// static 644HttpAuthHandlerNTLM::HostNameProc 645HttpAuthHandlerNTLM::get_host_name_proc_ = GetHostName; 646 647HttpAuthHandlerNTLM::HttpAuthHandlerNTLM() { 648} 649 650bool HttpAuthHandlerNTLM::NeedsIdentity() { 651 // This gets called for each round-trip. Only require identity on 652 // the first call (when auth_data_ is empty). On subsequent calls, 653 // we use the initially established identity. 654 return auth_data_.empty(); 655} 656 657bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() { 658 // Default credentials are not supported in the portable implementation of 659 // NTLM, but are supported in the SSPI implementation. 660 return false; 661} 662 663int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { 664 return OK; 665} 666 667HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() { 668 // Wipe our copy of the password from memory, to reduce the chance of being 669 // written to the paging file on disk. 670 ZapString(&password_); 671} 672 673// static 674HttpAuthHandlerNTLM::GenerateRandomProc 675HttpAuthHandlerNTLM::SetGenerateRandomProc( 676 GenerateRandomProc proc) { 677 GenerateRandomProc old_proc = generate_random_proc_; 678 generate_random_proc_ = proc; 679 return old_proc; 680} 681 682// static 683HttpAuthHandlerNTLM::HostNameProc HttpAuthHandlerNTLM::SetHostNameProc( 684 HostNameProc proc) { 685 HostNameProc old_proc = get_host_name_proc_; 686 get_host_name_proc_ = proc; 687 return old_proc; 688} 689 690HttpAuthHandlerNTLM::Factory::Factory() { 691} 692 693HttpAuthHandlerNTLM::Factory::~Factory() { 694} 695 696int HttpAuthHandlerNTLM::GetNextToken(const void* in_token, 697 uint32 in_token_len, 698 void** out_token, 699 uint32* out_token_len) { 700 int rv = 0; 701 702 // If in_token is non-null, then assume it contains a type 2 message... 703 if (in_token) { 704 LogToken("in-token", in_token, in_token_len); 705 std::string hostname = get_host_name_proc_(); 706 if (hostname.empty()) 707 return ERR_UNEXPECTED; 708 uint8 rand_buf[8]; 709 generate_random_proc_(rand_buf, 8); 710 rv = GenerateType3Msg(domain_, username_, password_, hostname, rand_buf, 711 in_token, in_token_len, out_token, out_token_len); 712 } else { 713 rv = GenerateType1Msg(out_token, out_token_len); 714 } 715 716 if (rv == OK) 717 LogToken("out-token", *out_token, *out_token_len); 718 719 return rv; 720} 721 722int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( 723 HttpAuth::ChallengeTokenizer* challenge, 724 HttpAuth::Target target, 725 const GURL& origin, 726 CreateReason reason, 727 int digest_nonce_count, 728 const BoundNetLog& net_log, 729 scoped_ptr<HttpAuthHandler>* handler) { 730 if (reason == CREATE_PREEMPTIVE) 731 return ERR_UNSUPPORTED_AUTH_SCHEME; 732 // TODO(cbentzel): Move towards model of parsing in the factory 733 // method and only constructing when valid. 734 // NOTE: Default credentials are not supported for the portable implementation 735 // of NTLM. 736 scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNTLM); 737 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) 738 return ERR_INVALID_RESPONSE; 739 handler->swap(tmp_handler); 740 return OK; 741} 742 743} // namespace net 744