OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017 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/ntlm_client.h" |
| 6 |
| 7 #include <string.h> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/md5.h" |
| 11 #include "net/base/zap.h" |
| 12 #include "net/http/des.h" |
| 13 #include "net/http/md4.h" |
| 14 #include "net/http/ntlm_buffer_writer.h" |
| 15 |
| 16 namespace net { |
| 17 |
| 18 NtlmClient::NtlmClient(uint32_t negotiate_flags) |
| 19 : negotiate_flags_(negotiate_flags) { |
| 20 // Just generate the negotiate message once and hold on to it. It never |
| 21 // changes and in a NTLMv2 it's used as an input |
| 22 // to the Message Integrity Check in the Authenticate message. |
| 23 GenerateNegotiateMessage(); |
| 24 } |
| 25 |
| 26 NtlmClient::~NtlmClient() {} |
| 27 |
| 28 void NtlmClient::GenerateNtlmHashV1(const base::string16& password, |
| 29 uint8_t* hash) { |
| 30 // hash param must have at least 16 bytes of space. |
| 31 NtlmBufferWriter writer(password.length() * 2); |
| 32 |
| 33 // The writer will handle the big endian case if necessary. |
| 34 writer.WriteUnicodeString(password); |
| 35 weak_crypto::MD4Sum(writer.GetBufferPtr(), writer.GetLength(), hash); |
| 36 ZapBuf(writer.GetBufferPtr(), writer.GetLength()); |
| 37 } |
| 38 |
| 39 void NtlmClient::GenerateResponseDesl(const uint8_t* hash, |
| 40 const uint8_t* challenge, |
| 41 uint8_t* response) { |
| 42 // hash param must be 16 bytes. |
| 43 // challenge param must be 8 bytes. |
| 44 // response param must be at least 24 bytes. |
| 45 |
| 46 // See DESL(K, D) function in Section 6 Appendix A of NTLMSSP specification. |
| 47 uint8_t key1[8]; |
| 48 uint8_t key2[8]; |
| 49 uint8_t key3[8]; |
| 50 |
| 51 // The last 2 bytes of the hash are zero padded (5 zeros) as the |
| 52 // input to generate key3. |
| 53 uint8_t padded_hash[7]; |
| 54 padded_hash[0] = hash[14]; |
| 55 padded_hash[1] = hash[15]; |
| 56 memset(padded_hash + 2, 0, 5); |
| 57 |
| 58 DESMakeKey(hash, key1); |
| 59 DESMakeKey(hash + 7, key2); |
| 60 DESMakeKey(padded_hash, key3); |
| 61 |
| 62 DESEncrypt(key1, challenge, response); |
| 63 DESEncrypt(key2, challenge, response + 8); |
| 64 DESEncrypt(key3, challenge, response + 16); |
| 65 } |
| 66 |
| 67 void NtlmClient::GenerateNtlmResponseV1(const base::string16& password, |
| 68 const uint8_t* challenge, |
| 69 uint8_t* ntlm_response) { |
| 70 uint8_t ntlm_hash[NtlmMessage::NTLM_HASH_LEN]; |
| 71 GenerateNtlmHashV1(password, ntlm_hash); |
| 72 GenerateResponseDesl(ntlm_hash, challenge, ntlm_response); |
| 73 } |
| 74 |
| 75 void NtlmClient::GenerateResponsesV1(const base::string16& password, |
| 76 const uint8_t* server_challenge, |
| 77 uint8_t* lm_response, |
| 78 uint8_t* ntlm_response) { |
| 79 GenerateNtlmResponseV1(password, server_challenge, ntlm_response); |
| 80 |
| 81 // In NTLM v1 (with LMv1 disabled), the lm_response and ntlm_response are the |
| 82 // same. So just copy the ntlm_response into the lm_response. |
| 83 memcpy(lm_response, ntlm_response, NtlmMessage::RESPONSE_V1_LEN); |
| 84 } |
| 85 |
| 86 void NtlmClient::GenerateLMResponseV1WithSS(const uint8_t* client_challenge, |
| 87 uint8_t* lm_response) { |
| 88 // In NTLM v1 with Session Security (aka NTLM2) the lm_response is 8 bytes of |
| 89 // client challenge and 16 bytes of zeros. (See 3.3.1) |
| 90 memcpy(lm_response, client_challenge, NtlmMessage::CHALLENGE_LEN); |
| 91 memset(lm_response + NtlmMessage::CHALLENGE_LEN, 0, |
| 92 NtlmMessage::RESPONSE_V1_LEN - NtlmMessage::CHALLENGE_LEN); |
| 93 } |
| 94 |
| 95 void NtlmClient::GenerateSessionHashV1WithSS(const uint8_t* server_challenge, |
| 96 const uint8_t* client_challenge, |
| 97 base::MD5Digest* session_hash) { |
| 98 base::MD5Context ctx; |
| 99 base::MD5Init(&ctx); |
| 100 base::MD5Update( |
| 101 &ctx, base::StringPiece(reinterpret_cast<const char*>(server_challenge), |
| 102 NtlmMessage::CHALLENGE_LEN)); |
| 103 base::MD5Update( |
| 104 &ctx, base::StringPiece(reinterpret_cast<const char*>(client_challenge), |
| 105 NtlmMessage::CHALLENGE_LEN)); |
| 106 |
| 107 base::MD5Final(session_hash, &ctx); |
| 108 } |
| 109 |
| 110 void NtlmClient::GenerateNtlmResponseV1WithSS(const base::string16& password, |
| 111 const uint8_t* server_challenge, |
| 112 const uint8_t* client_challenge, |
| 113 uint8_t* ntlm_response) { |
| 114 // Generate the NTLMv1 Hash. |
| 115 uint8_t ntlm_hash[NtlmMessage::NTLM_HASH_LEN]; |
| 116 GenerateNtlmHashV1(password, ntlm_hash); |
| 117 |
| 118 // Generate the NTLMv1 Session Hash. |
| 119 base::MD5Digest session_hash; |
| 120 GenerateSessionHashV1WithSS(server_challenge, client_challenge, |
| 121 &session_hash); |
| 122 |
| 123 // Only the first 8 bytes of |session_hash.a| are actually used. |
| 124 GenerateResponseDesl(ntlm_hash, session_hash.a, ntlm_response); |
| 125 } |
| 126 |
| 127 void NtlmClient::GenerateResponsesV1WithSS(const base::string16& password, |
| 128 const uint8_t* server_challenge, |
| 129 const uint8_t* client_challenge, |
| 130 uint8_t* lm_response, |
| 131 uint8_t* ntlm_response) { |
| 132 GenerateLMResponseV1WithSS(client_challenge, lm_response); |
| 133 GenerateNtlmResponseV1WithSS(password, server_challenge, client_challenge, |
| 134 ntlm_response); |
| 135 } |
| 136 |
| 137 void NtlmClient::GenerateNegotiateMessage() { |
| 138 NtlmBufferWriter writer(NtlmMessage::NEGOTIATE_MESSAGE_LEN); |
| 139 bool result = writer.WriteMessageHeader(NtlmMessage::MESSAGE_NEGOTIATE) && |
| 140 writer.WriteUInt32(negotiate_flags_) && |
| 141 writer.WriteEmptySecurityBuffer() && |
| 142 writer.WriteEmptySecurityBuffer() && writer.IsEndOfBuffer(); |
| 143 |
| 144 DCHECK(result); |
| 145 |
| 146 negotiate_message_.reset(writer.ReleaseBufferPtr()); |
| 147 } |
| 148 |
| 149 } // namespace net |
OLD | NEW |