| Index: net/http/ntlm_client.cc
|
| diff --git a/net/http/ntlm_client.cc b/net/http/ntlm_client.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..035c9650711a30bc3018da06339e1b779ea7d511
|
| --- /dev/null
|
| +++ b/net/http/ntlm_client.cc
|
| @@ -0,0 +1,149 @@
|
| +// Copyright (c) 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/http/ntlm_client.h"
|
| +
|
| +#include <string.h>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/md5.h"
|
| +#include "net/base/zap.h"
|
| +#include "net/http/des.h"
|
| +#include "net/http/md4.h"
|
| +#include "net/http/ntlm_buffer_writer.h"
|
| +
|
| +namespace net {
|
| +
|
| +NtlmClient::NtlmClient(uint32_t negotiate_flags)
|
| + : negotiate_flags_(negotiate_flags) {
|
| + // Just generate the negotiate message once and hold on to it. It never
|
| + // changes and in a NTLMv2 it's used as an input
|
| + // to the Message Integrity Check in the Authenticate message.
|
| + GenerateNegotiateMessage();
|
| +}
|
| +
|
| +NtlmClient::~NtlmClient() {}
|
| +
|
| +void NtlmClient::GenerateNtlmHashV1(const base::string16& password,
|
| + uint8_t* hash) {
|
| + // hash param must have at least 16 bytes of space.
|
| + NtlmBufferWriter writer(password.length() * 2);
|
| +
|
| + // The writer will handle the big endian case if necessary.
|
| + writer.WriteUnicodeString(password);
|
| + weak_crypto::MD4Sum(writer.GetBufferPtr(), writer.GetLength(), hash);
|
| + ZapBuf(writer.GetBufferPtr(), writer.GetLength());
|
| +}
|
| +
|
| +void NtlmClient::GenerateResponseDesl(const uint8_t* hash,
|
| + const uint8_t* challenge,
|
| + uint8_t* response) {
|
| + // hash param must be 16 bytes.
|
| + // challenge param must be 8 bytes.
|
| + // response param must be at least 24 bytes.
|
| +
|
| + // See DESL(K, D) function in Section 6 Appendix A of NTLMSSP specification.
|
| + uint8_t key1[8];
|
| + uint8_t key2[8];
|
| + uint8_t key3[8];
|
| +
|
| + // The last 2 bytes of the hash are zero padded (5 zeros) as the
|
| + // input to generate key3.
|
| + uint8_t padded_hash[7];
|
| + padded_hash[0] = hash[14];
|
| + padded_hash[1] = hash[15];
|
| + memset(padded_hash + 2, 0, 5);
|
| +
|
| + DESMakeKey(hash, key1);
|
| + DESMakeKey(hash + 7, key2);
|
| + DESMakeKey(padded_hash, key3);
|
| +
|
| + DESEncrypt(key1, challenge, response);
|
| + DESEncrypt(key2, challenge, response + 8);
|
| + DESEncrypt(key3, challenge, response + 16);
|
| +}
|
| +
|
| +void NtlmClient::GenerateNtlmResponseV1(const base::string16& password,
|
| + const uint8_t* challenge,
|
| + uint8_t* ntlm_response) {
|
| + uint8_t ntlm_hash[NtlmMessage::NTLM_HASH_LEN];
|
| + GenerateNtlmHashV1(password, ntlm_hash);
|
| + GenerateResponseDesl(ntlm_hash, challenge, ntlm_response);
|
| +}
|
| +
|
| +void NtlmClient::GenerateResponsesV1(const base::string16& password,
|
| + const uint8_t* server_challenge,
|
| + uint8_t* lm_response,
|
| + uint8_t* ntlm_response) {
|
| + GenerateNtlmResponseV1(password, server_challenge, ntlm_response);
|
| +
|
| + // In NTLM v1 (with LMv1 disabled), the lm_response and ntlm_response are the
|
| + // same. So just copy the ntlm_response into the lm_response.
|
| + memcpy(lm_response, ntlm_response, NtlmMessage::RESPONSE_V1_LEN);
|
| +}
|
| +
|
| +void NtlmClient::GenerateLMResponseV1WithSS(const uint8_t* client_challenge,
|
| + uint8_t* lm_response) {
|
| + // In NTLM v1 with Session Security (aka NTLM2) the lm_response is 8 bytes of
|
| + // client challenge and 16 bytes of zeros. (See 3.3.1)
|
| + memcpy(lm_response, client_challenge, NtlmMessage::CHALLENGE_LEN);
|
| + memset(lm_response + NtlmMessage::CHALLENGE_LEN, 0,
|
| + NtlmMessage::RESPONSE_V1_LEN - NtlmMessage::CHALLENGE_LEN);
|
| +}
|
| +
|
| +void NtlmClient::GenerateSessionHashV1WithSS(const uint8_t* server_challenge,
|
| + const uint8_t* client_challenge,
|
| + base::MD5Digest* session_hash) {
|
| + base::MD5Context ctx;
|
| + base::MD5Init(&ctx);
|
| + base::MD5Update(
|
| + &ctx, base::StringPiece(reinterpret_cast<const char*>(server_challenge),
|
| + NtlmMessage::CHALLENGE_LEN));
|
| + base::MD5Update(
|
| + &ctx, base::StringPiece(reinterpret_cast<const char*>(client_challenge),
|
| + NtlmMessage::CHALLENGE_LEN));
|
| +
|
| + base::MD5Final(session_hash, &ctx);
|
| +}
|
| +
|
| +void NtlmClient::GenerateNtlmResponseV1WithSS(const base::string16& password,
|
| + const uint8_t* server_challenge,
|
| + const uint8_t* client_challenge,
|
| + uint8_t* ntlm_response) {
|
| + // Generate the NTLMv1 Hash.
|
| + uint8_t ntlm_hash[NtlmMessage::NTLM_HASH_LEN];
|
| + GenerateNtlmHashV1(password, ntlm_hash);
|
| +
|
| + // Generate the NTLMv1 Session Hash.
|
| + base::MD5Digest session_hash;
|
| + GenerateSessionHashV1WithSS(server_challenge, client_challenge,
|
| + &session_hash);
|
| +
|
| + // Only the first 8 bytes of |session_hash.a| are actually used.
|
| + GenerateResponseDesl(ntlm_hash, session_hash.a, ntlm_response);
|
| +}
|
| +
|
| +void NtlmClient::GenerateResponsesV1WithSS(const base::string16& password,
|
| + const uint8_t* server_challenge,
|
| + const uint8_t* client_challenge,
|
| + uint8_t* lm_response,
|
| + uint8_t* ntlm_response) {
|
| + GenerateLMResponseV1WithSS(client_challenge, lm_response);
|
| + GenerateNtlmResponseV1WithSS(password, server_challenge, client_challenge,
|
| + ntlm_response);
|
| +}
|
| +
|
| +void NtlmClient::GenerateNegotiateMessage() {
|
| + NtlmBufferWriter writer(NtlmMessage::NEGOTIATE_MESSAGE_LEN);
|
| + bool result = writer.WriteMessageHeader(NtlmMessage::MESSAGE_NEGOTIATE) &&
|
| + writer.WriteUInt32(negotiate_flags_) &&
|
| + writer.WriteEmptySecurityBuffer() &&
|
| + writer.WriteEmptySecurityBuffer() && writer.IsEndOfBuffer();
|
| +
|
| + DCHECK(result);
|
| +
|
| + negotiate_message_.reset(writer.ReleaseBufferPtr());
|
| +}
|
| +
|
| +} // namespace net
|
|
|