Commit dd556619 authored by Zentaro Kavanagh's avatar Zentaro Kavanagh Committed by Commit Bot

Reland "Add a functionally equivalent NTLMv1 implementation."

This is a reland of 24f0eb87

Fix:
- Removed 2 incorrect asserts that were reading 
  uninitialized memory in unit tests.

Original change's description:
> Add a functionally equivalent NTLMv1 implementation.
> 
> - Replacement NTLMv1 implementation.
> - In preparation for new NTLMv2 implementation.
> - Passes all unit tests and has the same behavior.
> - Adds fuzzer for NtlmClient.
> - Code is still not live.
> 
> BUG=chromium:22532
> TEST=All unit tests pass.
> 
> Change-Id: Idae533312805d21d49943fd634d54af3a93a7c00
> Reviewed-on: https://chromium-review.googlesource.com/598307
> Commit-Queue: Zentaro Kavanagh <zentaro@google.com>
> Reviewed-by: Asanka Herath <asanka@chromium.org>
> Reviewed-by: Matt Menke <mmenke@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#491611}

Bug: chromium:22532
Change-Id: I425914904d13d94214d4249ff75648775bf1c84c
Reviewed-on: https://chromium-review.googlesource.com/600388Reviewed-by: default avatarAsanka Herath <asanka@chromium.org>
Commit-Queue: Zentaro Kavanagh <zentaro@google.com>
Cr-Commit-Position: refs/heads/master@{#491828}
parent f8ca0167
......@@ -1041,6 +1041,8 @@ component("net") {
"ntlm/ntlm_buffer_reader.h",
"ntlm/ntlm_buffer_writer.cc",
"ntlm/ntlm_buffer_writer.h",
"ntlm/ntlm_client.cc",
"ntlm/ntlm_client.h",
"ntlm/ntlm_constants.h",
"proxy/dhcp_proxy_script_adapter_fetcher_win.cc",
"proxy/dhcp_proxy_script_adapter_fetcher_win.h",
......@@ -1797,6 +1799,8 @@ component("net") {
"ntlm/ntlm_buffer_reader.h",
"ntlm/ntlm_buffer_writer.cc",
"ntlm/ntlm_buffer_writer.h",
"ntlm/ntlm_client.cc",
"ntlm/ntlm_client.h",
"ntlm/ntlm_constants.h",
]
}
......@@ -4898,6 +4902,7 @@ test("net_unittests") {
"ntlm/des_unittest.cc",
"ntlm/ntlm_buffer_reader_unittest.cc",
"ntlm/ntlm_buffer_writer_unittest.cc",
"ntlm/ntlm_client_unittest.cc",
"ntlm/ntlm_test_data.h",
"ntlm/ntlm_unittest.cc",
"proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc",
......@@ -5316,6 +5321,7 @@ test("net_unittests") {
"ntlm/des_unittest.cc",
"ntlm/ntlm_buffer_reader_unittest.cc",
"ntlm/ntlm_buffer_writer_unittest.cc",
"ntlm/ntlm_client_unittest.cc",
"ntlm/ntlm_test_data.h",
"ntlm/ntlm_unittest.cc",
]
......@@ -5878,6 +5884,21 @@ fuzzer_test("net_http_stream_parser_fuzzer") {
dict = "data/fuzzer_dictionaries/net_http_stream_parser_fuzzer.dict"
}
fuzzer_test("net_ntlm_ntlm_client_fuzzer") {
sources = [
"ntlm/ntlm_client_fuzzer.cc",
]
deps = [
":net_fuzzer_test_support",
":test_support",
"//base",
"//net",
"//net:net_unittests",
]
dict = "data/fuzzer_dictionaries/net_ntlm_ntlm_client_fuzzer.dict"
seed_corpus = "data/fuzzer_data/ntlm_client_fuzzer/"
}
if (!disable_brotli_filter) {
fuzzer_test("net_brotli_source_stream_fuzzer") {
sources = [
......
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
# Copyright 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.
# Signature
"NTLMSSP\x00"
# Message Types
"\x01\x00\x00\x00"
"\x02\x00\x00\x00"
"\x03\x00\x00\x00"
# Common Target Name offset
"\x20\x00\x00\x00"
# Useful security buffer lengths to match "Server"
"\x06\x00"
"\x0c\x00"
# Common\interesting flag values
"\x07\x82\x08\x00"
"\x06\x82\x08\x00"
"\x07\x82\x00\x00"
"\x06\x82\x00\x00"
"\x01\x00\x08\x00"
"\x02\x00\x08\x00"
"\x03\x00\x08\x00"
"\x01\x00\x00\x00"
"\x02\x00\x00\x00"
"\x03\x00\x00\x00"
"\x08\x00"
"\x00\x00"
# Target Names
"Server"
"S\x00e\x00r\x00v\x00e\x00r\x00"
\ No newline at end of file
// Copyright 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/ntlm/ntlm_client.h"
#include <string.h>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "net/ntlm/ntlm.h"
#include "net/ntlm/ntlm_buffer_reader.h"
#include "net/ntlm/ntlm_buffer_writer.h"
namespace net {
namespace ntlm {
namespace {
// Parses the challenge message and returns the |challenge_flags| and
// |server_challenge| into the supplied buffer.
// |server_challenge| must contain at least 8 bytes.
bool ParseChallengeMessage(const Buffer& challenge_message,
NegotiateFlags* challenge_flags,
uint8_t* server_challenge) {
NtlmBufferReader challenge_reader(challenge_message);
return challenge_reader.MatchMessageHeader(MessageType::kChallenge) &&
challenge_reader.SkipSecurityBufferWithValidation() &&
challenge_reader.ReadFlags(challenge_flags) &&
challenge_reader.ReadBytes(server_challenge, kChallengeLen);
}
bool WriteAuthenticateMessage(NtlmBufferWriter* authenticate_writer,
SecurityBuffer lm_payload,
SecurityBuffer ntlm_payload,
SecurityBuffer domain_payload,
SecurityBuffer username_payload,
SecurityBuffer hostname_payload,
NegotiateFlags authenticate_flags) {
return authenticate_writer->WriteMessageHeader(MessageType::kAuthenticate) &&
authenticate_writer->WriteSecurityBuffer(lm_payload) &&
authenticate_writer->WriteSecurityBuffer(ntlm_payload) &&
authenticate_writer->WriteSecurityBuffer(domain_payload) &&
authenticate_writer->WriteSecurityBuffer(username_payload) &&
authenticate_writer->WriteSecurityBuffer(hostname_payload) &&
authenticate_writer->WriteSecurityBuffer(
SecurityBuffer(kAuthenticateHeaderLenV1, 0)) &&
authenticate_writer->WriteFlags(authenticate_flags);
}
bool WriteResponsePayloads(NtlmBufferWriter* authenticate_writer,
const uint8_t* lm_response,
size_t lm_response_len,
const uint8_t* ntlm_response,
size_t ntlm_response_len) {
return authenticate_writer->WriteBytes(lm_response, lm_response_len) &&
authenticate_writer->WriteBytes(ntlm_response, ntlm_response_len);
}
bool WriteStringPayloads(NtlmBufferWriter* authenticate_writer,
bool is_unicode,
const base::string16& domain,
const base::string16& username,
const std::string& hostname) {
if (is_unicode) {
return authenticate_writer->WriteUtf16String(domain) &&
authenticate_writer->WriteUtf16String(username) &&
authenticate_writer->WriteUtf8AsUtf16String(hostname);
} else {
return authenticate_writer->WriteUtf16AsUtf8String(domain) &&
authenticate_writer->WriteUtf16AsUtf8String(username) &&
authenticate_writer->WriteUtf8String(hostname);
}
}
// Returns the size in bytes of a string16 depending whether unicode
// was negotiated.
size_t GetStringPayloadLength(const base::string16& str, bool is_unicode) {
if (is_unicode)
return str.length() * 2;
// When |WriteUtf16AsUtf8String| is called with a |base::string16|, the string
// is converted to UTF8. Do the conversion to ensure that the character
// count is correct.
return base::UTF16ToUTF8(str).length();
}
// Returns the size in bytes of a std::string depending whether unicode
// was negotiated.
size_t GetStringPayloadLength(const std::string& str, bool is_unicode) {
if (!is_unicode)
return str.length();
return base::UTF8ToUTF16(str).length() * 2;
}
} // namespace
NtlmClient::NtlmClient() : negotiate_flags_(kNegotiateMessageFlags) {
// 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() {}
Buffer NtlmClient::GetNegotiateMessage() const {
return negotiate_message_;
}
void NtlmClient::GenerateNegotiateMessage() {
NtlmBufferWriter writer(kNegotiateMessageLen);
bool result =
writer.WriteMessageHeader(MessageType::kNegotiate) &&
writer.WriteFlags(negotiate_flags_) &&
writer.WriteSecurityBuffer(SecurityBuffer(kNegotiateMessageLen, 0)) &&
writer.WriteSecurityBuffer(SecurityBuffer(kNegotiateMessageLen, 0)) &&
writer.IsEndOfBuffer();
DCHECK(result);
negotiate_message_ = writer.Pass();
}
Buffer NtlmClient::GenerateAuthenticateMessage(
const base::string16& domain,
const base::string16& username,
const base::string16& password,
const std::string& hostname,
const uint8_t* client_challenge,
const Buffer& server_challenge_message) const {
// Limit the size of strings that are accepted. As an absolute limit any
// field represented by a |SecurityBuffer| or |AvPair| must be less than
// UINT16_MAX bytes long. The strings are restricted to the maximum sizes
// without regard to encoding. As such this isn't intended to restrict all
// invalid inputs, only to allow all possible valid inputs.
//
// |domain| and |hostname| can be no longer than 255 characters.
// |username| can be no longer than 104 characters. See [1].
// |password| can be no longer than 256 characters. See [2].
//
// [1] - https://technet.microsoft.com/en-us/library/bb726984.aspx
// [2] - https://technet.microsoft.com/en-us/library/cc512606.aspx
if (hostname.length() > kMaxFqdnLen || domain.length() > kMaxFqdnLen ||
username.length() > kMaxUsernameLen ||
password.length() > kMaxPasswordLen)
return Buffer();
NegotiateFlags challenge_flags;
uint8_t server_challenge[kChallengeLen];
// Read the flags and the server's random challenge from the challenge
// message.
if (!ParseChallengeMessage(server_challenge_message, &challenge_flags,
server_challenge)) {
return Buffer();
}
// Calculate the responses for the authenticate message.
uint8_t lm_response[kResponseLenV1];
uint8_t ntlm_response[kResponseLenV1];
// Always use extended session security even if the server tries to downgrade.
NegotiateFlags authenticate_flags = (challenge_flags & negotiate_flags_) |
NegotiateFlags::kExtendedSessionSecurity;
// Generate the LM and NTLM responses.
GenerateResponsesV1WithSessionSecurity(
password, server_challenge, client_challenge, lm_response, ntlm_response);
// Calculate all the payload lengths and offsets.
bool is_unicode = (authenticate_flags & NegotiateFlags::kUnicode) ==
NegotiateFlags::kUnicode;
SecurityBuffer lm_info;
SecurityBuffer ntlm_info;
SecurityBuffer domain_info;
SecurityBuffer username_info;
SecurityBuffer hostname_info;
size_t authenticate_message_len;
CalculatePayloadLayout(is_unicode, domain, username, hostname, &lm_info,
&ntlm_info, &domain_info, &username_info,
&hostname_info, &authenticate_message_len);
NtlmBufferWriter authenticate_writer(authenticate_message_len);
bool writer_result = WriteAuthenticateMessage(
&authenticate_writer, lm_info, ntlm_info, domain_info, username_info,
hostname_info, authenticate_flags);
DCHECK(writer_result);
DCHECK_EQ(authenticate_writer.GetCursor(), GetAuthenticateHeaderLength());
writer_result =
WriteResponsePayloads(&authenticate_writer, lm_response, lm_info.length,
ntlm_response, ntlm_info.length);
DCHECK(writer_result);
DCHECK_EQ(authenticate_writer.GetCursor(), domain_info.offset);
writer_result = WriteStringPayloads(&authenticate_writer, is_unicode, domain,
username, hostname);
DCHECK(writer_result);
DCHECK(authenticate_writer.IsEndOfBuffer());
DCHECK_EQ(authenticate_message_len, authenticate_writer.GetLength());
return authenticate_writer.Pass();
}
void NtlmClient::CalculatePayloadLayout(
bool is_unicode,
const base::string16& domain,
const base::string16& username,
const std::string& hostname,
SecurityBuffer* lm_info,
SecurityBuffer* ntlm_info,
SecurityBuffer* domain_info,
SecurityBuffer* username_info,
SecurityBuffer* hostname_info,
size_t* authenticate_message_len) const {
size_t upto = GetAuthenticateHeaderLength();
lm_info->offset = upto;
lm_info->length = kResponseLenV1;
upto += lm_info->length;
ntlm_info->offset = upto;
ntlm_info->length = GetNtlmResponseLength();
upto += ntlm_info->length;
domain_info->offset = upto;
domain_info->length = GetStringPayloadLength(domain, is_unicode);
upto += domain_info->length;
username_info->offset = upto;
username_info->length = GetStringPayloadLength(username, is_unicode);
upto += username_info->length;
hostname_info->offset = upto;
hostname_info->length = GetStringPayloadLength(hostname, is_unicode);
upto += hostname_info->length;
*authenticate_message_len = upto;
}
size_t NtlmClient::GetAuthenticateHeaderLength() const {
return kAuthenticateHeaderLenV1;
}
size_t NtlmClient::GetNtlmResponseLength() const {
return kResponseLenV1;
}
} // namespace ntlm
} // namespace net
\ No newline at end of file
// Copyright 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.
// Based on [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol
// Specification version 28.0 [1]. Additional NTLM reference [2].
//
// [1] https://msdn.microsoft.com/en-us/library/cc236621.aspx
// [2] http://davenport.sourceforge.net/ntlm.html
#ifndef NET_BASE_NTLM_CLIENT_H_
#define NET_BASE_NTLM_CLIENT_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
#include "net/ntlm/ntlm_constants.h"
namespace net {
namespace ntlm {
// Provides an implementation of an NTLMv1 Client.
//
// The implementation supports NTLMv1 with extended session security (NTLM2).
class NET_EXPORT_PRIVATE NtlmClient {
public:
NtlmClient();
~NtlmClient();
// Returns a |Buffer| containing the Negotiate message.
Buffer GetNegotiateMessage() const;
// Returns a |Buffer| containing the Authenticate message. If the method
// fails an empty |Buffer| is returned.
//
// |hostname| can be a short NetBIOS name or an FQDN, however the server will
// only inspect this field if the default domain policy is to restrict NTLM.
// In this case the hostname will be compared to a whitelist stored in this
// group policy [1].
// |client_challenge| must contain 8 bytes of random data.
// |server_challenge_message| is the full content of the challenge message
// sent by the server.
//
// [1] - https://technet.microsoft.com/en-us/library/jj852267(v=ws.11).aspx
Buffer GenerateAuthenticateMessage(
const base::string16& domain,
const base::string16& username,
const base::string16& password,
const std::string& hostname,
const uint8_t* client_challenge,
const Buffer& server_challenge_message) const;
private:
// Calculates the lengths and offset for all the payloads in the message.
void CalculatePayloadLayout(bool is_unicode,
const base::string16& domain,
const base::string16& username,
const std::string& hostname,
SecurityBuffer* lm_info,
SecurityBuffer* ntlm_info,
SecurityBuffer* domain_info,
SecurityBuffer* username_info,
SecurityBuffer* hostname_info,
size_t* authenticate_message_len) const;
// Returns the length of the header part of the Authenticate message.
// NOTE: When NTLMv2 support is added this is no longer a fixed value.
size_t GetAuthenticateHeaderLength() const;
// Returns the length of the NTLM response.
// NOTE: When NTLMv2 support is added this is no longer a fixed value.
size_t GetNtlmResponseLength() const;
// Generates the negotiate message (which is always the same) into
// |negotiate_message_|.
void GenerateNegotiateMessage();
NegotiateFlags negotiate_flags_;
Buffer negotiate_message_;
DISALLOW_COPY_AND_ASSIGN(NtlmClient);
};
} // namespace ntlm
} // namespace net
#endif // NET_BASE_NTLM_CLIENT_H_
\ No newline at end of file
// Copyright 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 <stddef.h>
#include <stdint.h>
#include "base/test/fuzzed_data_provider.h"
#include "net/ntlm/ntlm_client.h"
#include "net/ntlm/ntlm_test_data.h"
base::string16 ConsumeRandomLengthString16(
base::FuzzedDataProvider& data_provider,
size_t max_chars) {
std::string bytes = data_provider.ConsumeRandomLengthString(max_chars * 2);
return base::string16(reinterpret_cast<const base::char16*>(bytes.data()),
bytes.size() / 2);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
net::ntlm::NtlmClient client;
// Generate the input strings and challenge message. The strings will have a
// maximum length 1 character longer than the maximum that |NtlmClient| will
// accept to allow exploring the error cases.
base::FuzzedDataProvider fdp(data, size);
base::string16 domain =
ConsumeRandomLengthString16(fdp, net::ntlm::kMaxFqdnLen + 1);
base::string16 username =
ConsumeRandomLengthString16(fdp, net::ntlm::kMaxUsernameLen + 1);
base::string16 password =
ConsumeRandomLengthString16(fdp, net::ntlm::kMaxPasswordLen + 1);
std::string hostname =
fdp.ConsumeRandomLengthString(net::ntlm::kMaxFqdnLen + 1);
std::string challenge_msg_bytes = fdp.ConsumeRemainingBytes();
client.GenerateAuthenticateMessage(
domain, username, password, hostname, net::ntlm::test::kClientChallenge,
net::ntlm::Buffer(
reinterpret_cast<const uint8_t*>(challenge_msg_bytes.data()),
challenge_msg_bytes.size()));
return 0;
}
\ No newline at end of file
// Copyright 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/ntlm/ntlm_client.h"
#include <string>
#include "build/build_config.h"
#include "net/ntlm/ntlm.h"
#include "net/ntlm/ntlm_buffer_reader.h"
#include "net/ntlm/ntlm_buffer_writer.h"
#include "net/ntlm/ntlm_test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace ntlm {
namespace {
Buffer GenerateAuthMsg(const NtlmClient& client, const Buffer& challenge_msg) {
return client.GenerateAuthenticateMessage(
test::kNtlmDomain, test::kUser, test::kPassword, test::kHostnameAscii,
test::kClientChallenge, challenge_msg);
}
Buffer GenerateAuthMsg(const NtlmClient& client,
const uint8_t* challenge_msg,
size_t challenge_msg_len) {
return GenerateAuthMsg(client, Buffer(challenge_msg, challenge_msg_len));
}
Buffer GenerateAuthMsg(const NtlmClient& client,
const NtlmBufferWriter& challenge_writer) {
return GenerateAuthMsg(client, challenge_writer.GetBuffer());
}
bool GetAuthMsgResult(const NtlmClient& client,
const NtlmBufferWriter& challenge_writer) {
return !GenerateAuthMsg(client, challenge_writer).empty();
}
bool ReadBytesPayload(NtlmBufferReader* reader, uint8_t* buffer, size_t len) {
SecurityBuffer sec_buf;
return reader->ReadSecurityBuffer(&sec_buf) && (sec_buf.length == len) &&
reader->ReadBytesFrom(sec_buf, buffer);
}
// Reads bytes from a payload and assigns them to a string. This makes
// no assumptions about the underlying encoding.
bool ReadStringPayload(NtlmBufferReader* reader, std::string* str) {
SecurityBuffer sec_buf;
if (!reader->ReadSecurityBuffer(&sec_buf))
return false;
std::unique_ptr<uint8_t[]> raw(new uint8_t[sec_buf.length]);
if (!reader->ReadBytesFrom(sec_buf, raw.get()))
return false;
str->assign(reinterpret_cast<const char*>(raw.get()), sec_buf.length);
return true;
}
// Reads bytes from a payload and assigns them to a string16. This makes
// no assumptions about the underlying encoding. This will fail if there
// are an odd number of bytes in the payload.
bool ReadString16Payload(NtlmBufferReader* reader, base::string16* str) {
SecurityBuffer sec_buf;
if (!reader->ReadSecurityBuffer(&sec_buf) || (sec_buf.length % 2 != 0))
return false;
std::unique_ptr<uint8_t[]> raw(new uint8_t[sec_buf.length]);
if (!reader->ReadBytesFrom(sec_buf, raw.get()))
return false;
#if defined(ARCH_CPU_BIG_ENDIAN)
for (size_t i = 0; i < sec_buf.length; i += 2) {
std::swap(raw.get()[i], raw.get()[i + 1]);
}
#endif
str->assign(reinterpret_cast<const base::char16*>(raw.get()),
sec_buf.length / 2);
return true;
}
} // namespace
TEST(NtlmClientTest, VerifyNegotiateMessageV1) {
NtlmClient client;
Buffer result = client.GetNegotiateMessage();
ASSERT_EQ(kNegotiateMessageLen, result.size());
ASSERT_EQ(0, memcmp(test::kExpectedNegotiateMsg, result.data(),
kNegotiateMessageLen));
}
TEST(NtlmClientTest, MinimalStructurallyValidChallenge) {
NtlmClient client;
NtlmBufferWriter writer(kMinChallengeHeaderLen);
ASSERT_TRUE(
writer.WriteBytes(test::kMinChallengeMessage, kMinChallengeHeaderLen));
ASSERT_TRUE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, MinimalStructurallyValidChallengeZeroOffset) {
NtlmClient client;
// The spec (2.2.1.2) states that the length SHOULD be 0 and the offset
// SHOULD be where the payload would be if it was present. This is the
// expected response from a compliant server when no target name is sent.
// In reality the offset should always be ignored if the length is zero.
// Also implementations often just write zeros.
uint8_t raw[kMinChallengeHeaderLen];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Modify the default valid message to overwrite the offset to zero.
ASSERT_NE(0x00, raw[16]);
raw[16] = 0x00;
NtlmBufferWriter writer(kMinChallengeHeaderLen);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
ASSERT_TRUE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, ChallengeMsgTooShort) {
NtlmClient client;
// Fail because the minimum size valid message is 32 bytes.
NtlmBufferWriter writer(kMinChallengeHeaderLen - 1);
ASSERT_TRUE(writer.WriteBytes(test::kMinChallengeMessage,
kMinChallengeHeaderLen - 1));
ASSERT_FALSE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, ChallengeMsgNoSig) {
NtlmClient client;
// Fail because the first 8 bytes don't match "NTLMSSP\0"
uint8_t raw[kMinChallengeHeaderLen];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Modify the default valid message to overwrite the last byte of the
// signature.
ASSERT_NE(0xff, raw[7]);
raw[7] = 0xff;
NtlmBufferWriter writer(kMinChallengeHeaderLen);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
ASSERT_FALSE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, ChallengeMsgWrongMessageType) {
NtlmClient client;
// Fail because the message type should be MessageType::kChallenge
// (0x00000002)
uint8_t raw[kMinChallengeHeaderLen];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Modify the message type.
ASSERT_NE(0x03, raw[8]);
raw[8] = 0x03;
NtlmBufferWriter writer(kMinChallengeHeaderLen);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
ASSERT_FALSE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, ChallengeWithNoTargetName) {
NtlmClient client;
// The spec (2.2.1.2) states that the length SHOULD be 0 and the offset
// SHOULD be where the payload would be if it was present. This is the
// expected response from a compliant server when no target name is sent.
// In reality the offset should always be ignored if the length is zero.
// Also implementations often just write zeros.
uint8_t raw[kMinChallengeHeaderLen];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Modify the default valid message to overwrite the offset to zero.
ASSERT_NE(0x00, raw[16]);
raw[16] = 0x00;
NtlmBufferWriter writer(kMinChallengeHeaderLen);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
ASSERT_TRUE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, Type2MessageWithTargetName) {
NtlmClient client;
// One extra byte is provided for target name.
uint8_t raw[kMinChallengeHeaderLen + 1];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Put something in the target name.
raw[kMinChallengeHeaderLen] = 'Z';
// Modify the default valid message to indicate 1 byte is present in the
// target name payload.
ASSERT_NE(0x01, raw[12]);
ASSERT_EQ(0x00, raw[13]);
ASSERT_NE(0x01, raw[14]);
ASSERT_EQ(0x00, raw[15]);
raw[12] = 0x01;
raw[14] = 0x01;
NtlmBufferWriter writer(kChallengeHeaderLen + 1);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
ASSERT_TRUE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, NoTargetNameOverflowFromOffset) {
NtlmClient client;
uint8_t raw[kMinChallengeHeaderLen];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Modify the default valid message to claim that the target name field is 1
// byte long overrunning the end of the message message.
ASSERT_NE(0x01, raw[12]);
ASSERT_EQ(0x00, raw[13]);
ASSERT_NE(0x01, raw[14]);
ASSERT_EQ(0x00, raw[15]);
raw[12] = 0x01;
raw[14] = 0x01;
NtlmBufferWriter writer(kMinChallengeHeaderLen);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
// The above malformed message could cause an implementation to read outside
// the message buffer because the offset is past the end of the message.
// Verify it gets rejected.
ASSERT_FALSE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, NoTargetNameOverflowFromLength) {
NtlmClient client;
// Message has 1 extra byte of space after the header for the target name.
// One extra byte is provided for target name.
uint8_t raw[kMinChallengeHeaderLen + 1];
memcpy(raw, test::kMinChallengeMessage, kMinChallengeHeaderLen);
// Put something in the target name.
raw[kMinChallengeHeaderLen] = 'Z';
// Modify the default valid message to indicate 2 bytes are present in the
// target name payload (however there is only space for 1).
ASSERT_NE(0x02, raw[12]);
ASSERT_EQ(0x00, raw[13]);
ASSERT_NE(0x02, raw[14]);
ASSERT_EQ(0x00, raw[15]);
raw[12] = 0x02;
raw[14] = 0x02;
NtlmBufferWriter writer(kMinChallengeHeaderLen + 1);
ASSERT_TRUE(writer.WriteBytes(raw, arraysize(raw)));
// The above malformed message could cause an implementation
// to read outside the message buffer because the length is
// longer than available space. Verify it gets rejected.
ASSERT_FALSE(GetAuthMsgResult(client, writer));
}
TEST(NtlmClientTest, Type3UnicodeWithSessionSecuritySpecTest) {
NtlmClient client;
Buffer result = GenerateAuthMsg(client, test::kChallengeMsgV1,
arraysize(test::kChallengeMsgV1));
ASSERT_FALSE(result.empty());
ASSERT_EQ(arraysize(test::kExpectedAuthenticateMsgV1), result.size());
ASSERT_EQ(0, memcmp(test::kExpectedAuthenticateMsgV1, result.data(),
result.size()));
}
TEST(NtlmClientTest, Type3WithoutUnicode) {
NtlmClient client;
Buffer result = GenerateAuthMsg(client, test::kMinChallengeMessageNoUnicode,
kMinChallengeHeaderLen);
ASSERT_FALSE(result.empty());
NtlmBufferReader reader(result);
ASSERT_TRUE(reader.MatchMessageHeader(MessageType::kAuthenticate));
// Read the LM and NTLM Response Payloads.
uint8_t actual_lm_response[kResponseLenV1];
uint8_t actual_ntlm_response[kResponseLenV1];
ASSERT_TRUE(ReadBytesPayload(&reader, actual_lm_response, kResponseLenV1));
ASSERT_TRUE(ReadBytesPayload(&reader, actual_ntlm_response, kResponseLenV1));
ASSERT_EQ(0, memcmp(test::kExpectedLmResponseWithV1SS, actual_lm_response,
kResponseLenV1));
ASSERT_EQ(0, memcmp(test::kExpectedNtlmResponseWithV1SS, actual_ntlm_response,
kResponseLenV1));
std::string domain;
std::string username;
std::string hostname;
ASSERT_TRUE(ReadStringPayload(&reader, &domain));
ASSERT_EQ(test::kNtlmDomainAscii, domain);
ASSERT_TRUE(ReadStringPayload(&reader, &username));
ASSERT_EQ(test::kUserAscii, username);
ASSERT_TRUE(ReadStringPayload(&reader, &hostname));
ASSERT_EQ(test::kHostnameAscii, hostname);
// The session key is not used in HTTP. Since NTLMSSP_NEGOTIATE_KEY_EXCH
// was not sent this is empty.
ASSERT_TRUE(reader.MatchEmptySecurityBuffer());
// Verify the unicode flag is not set and OEM flag is.
NegotiateFlags flags;
ASSERT_TRUE(reader.ReadFlags(&flags));
ASSERT_EQ(NegotiateFlags::kNone, flags & NegotiateFlags::kUnicode);
ASSERT_EQ(NegotiateFlags::kOem, flags & NegotiateFlags::kOem);
}
TEST(NtlmClientTest, ClientDoesNotDowngradeSessionSecurity) {
NtlmClient client;
Buffer result = GenerateAuthMsg(client, test::kMinChallengeMessageNoSS,
kMinChallengeHeaderLen);
ASSERT_FALSE(result.empty());
NtlmBufferReader reader(result);
ASSERT_TRUE(reader.MatchMessageHeader(MessageType::kAuthenticate));
// Read the LM and NTLM Response Payloads.
uint8_t actual_lm_response[kResponseLenV1];
uint8_t actual_ntlm_response[kResponseLenV1];
ASSERT_TRUE(ReadBytesPayload(&reader, actual_lm_response, kResponseLenV1));
ASSERT_TRUE(ReadBytesPayload(&reader, actual_ntlm_response, kResponseLenV1));
// The important part of this test is that even though the
// server told the client to drop session security. The client
// DID NOT drop it.
ASSERT_EQ(0, memcmp(test::kExpectedLmResponseWithV1SS, actual_lm_response,
kResponseLenV1));
ASSERT_EQ(0, memcmp(test::kExpectedNtlmResponseWithV1SS, actual_ntlm_response,
kResponseLenV1));
base::string16 domain;
base::string16 username;
base::string16 hostname;
ASSERT_TRUE(ReadString16Payload(&reader, &domain));
ASSERT_EQ(test::kNtlmDomain, domain);
ASSERT_TRUE(ReadString16Payload(&reader, &username));
ASSERT_EQ(test::kUser, username);
ASSERT_TRUE(ReadString16Payload(&reader, &hostname));
ASSERT_EQ(test::kHostname, hostname);
// The session key is not used in HTTP. Since NTLMSSP_NEGOTIATE_KEY_EXCH
// was not sent this is empty.
ASSERT_TRUE(reader.MatchEmptySecurityBuffer());
// Verify the unicode and session security flag is set.
NegotiateFlags flags;
ASSERT_TRUE(reader.ReadFlags(&flags));
ASSERT_EQ(NegotiateFlags::kUnicode, flags & NegotiateFlags::kUnicode);
ASSERT_EQ(NegotiateFlags::kExtendedSessionSecurity,
flags & NegotiateFlags::kExtendedSessionSecurity);
}
} // namespace ntlm
} // namespace net
......@@ -38,7 +38,7 @@ enum class NtlmVersion {
};
// There are 3 types of messages in NTLM. The message type is a field in
// every NTLM message header.
// every NTLM message header. See [MS-NLMP] Section 2.2.
enum class MessageType : uint32_t {
kNegotiate = 0x01,
kChallenge = 0x02,
......@@ -78,10 +78,15 @@ static constexpr size_t kSignatureLen = arraysize(kSignature);
static constexpr size_t kSecurityBufferLen =
(2 * sizeof(uint16_t)) + sizeof(uint32_t);
static constexpr size_t kNegotiateMessageLen = 32;
static constexpr size_t kMinChallengeHeaderLen = 32;
static constexpr size_t kChallengeHeaderLen = 32;
static constexpr size_t kResponseLenV1 = 24;
static constexpr size_t kChallengeLen = 8;
static constexpr size_t kNtlmHashLen = 16;
static constexpr size_t kAuthenticateHeaderLenV1 = 64;
static constexpr size_t kMaxFqdnLen = 255;
static constexpr size_t kMaxUsernameLen = 104;
static constexpr size_t kMaxPasswordLen = 256;
static constexpr NegotiateFlags kNegotiateMessageFlags =
NegotiateFlags::kUnicode | NegotiateFlags::kOem |
......
......@@ -35,6 +35,15 @@ constexpr uint8_t kServerChallenge[kChallengeLen] = {0x01, 0x23, 0x45, 0x67,
constexpr uint8_t kClientChallenge[kChallengeLen] = {0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa};
// Test input defined in [MS-NLMP] Section 4.2.3.3.
constexpr uint8_t kChallengeMsgV1[] = {
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x33, 0x82, 0x0a, 0x82,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00,
0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00};
// A minimal challenge message for tests. For NTLMv1 this implementation only
// reads the smallest required version of the message (32 bytes). Some
// servers may still send messages this small. The only relevant flags
......@@ -54,6 +63,20 @@ constexpr uint8_t kMinChallengeMessage[kChallengeHeaderLen] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, 0x82,
0x08, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
// The same message as |kMinChallengeMessage| but with the
// NTLMSSP_NEGOTIATE_UNICODE flag cleared.
constexpr uint8_t kMinChallengeMessageNoUnicode[kChallengeHeaderLen] = {
'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0', 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x82,
0x08, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
// The same message as |kMinChallengeMessage| but with the
// NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag cleared.
constexpr uint8_t kMinChallengeMessageNoSS[kChallengeHeaderLen] = {
'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0', 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, 0x82,
0x00, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
// Test result value for NTOWFv1() defined in [MS-NLMP] Section 4.2.2.1.2.
constexpr uint8_t kExpectedNtlmHashV1[kNtlmHashLen] = {
0xa4, 0xf4, 0x9c, 0x40, 0x65, 0x10, 0xbd, 0xca,
......@@ -74,6 +97,80 @@ constexpr uint8_t kExpectedLmResponseWithV1SS[kResponseLenV1] = {
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Expected negotiate message from this implementation.
// [0-7] - "NTLMSSP\0" (Signature)
// [9-11] - |MessageType::NEGOTIATE| (Message Type = 0x00000001)
// [12-15] - |NEGOTIATE_MESSAGE_FLAGS| (Flags = 0x00088207)
// [16-23] - |SecBuf(kNegotiateMessageLen, 0)|(Domain)
// [24-32] - |SecBuf(kNegotiateMessageLen, 0)|(Workstation)
//
// NOTE: Message does not include Version field. Since
// NTLMSSP_NEGOTIATE_VERSION is never sent, it is not required, and the server
// won't try to read it. The field is currently omitted for test compatibility
// with the existing implementation. When NTLMv2 is implemented this field
// will be present for both NTLMv1 and NTLMv2, however it will always be set to
// all zeros. The version field is only used for debugging and only defines
// a mapping to Windows operating systems.
//
// Similarly both Domain and Workstation fields are are not strictly required
// either (though are included here) since neither
// NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED nor
// NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED are ever sent. A compliant server
// should never read past the 16th byte in this message.
//
// See [MS-NLMP] Section 2.2.2.5 for more detail on flags and 2.2.2.1 for the
// Negotiate message in general.
constexpr uint8_t kExpectedNegotiateMsg[kNegotiateMessageLen] = {
'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0', 0x01, 0x00, 0x00,
0x00, 0x07, 0x82, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00};
// Expected V1 Authenticate message from this implementation when sent
// |kChallengeMsgV1| as the challenge.
//
// [0-7] - "NTLMSSP\0" (Signature)
// [9-11] - |MessageType::kAuthenticate| (Message Type = 0x00000003)
// [12-19] - |SecBuf(64, RESPONSE_V1_LEN)| (LM Response)
// [20-27] - |SecBuf(88, RESPONSE_V1_LEN)| (NTLM Response)
// [28-35] - |SecBuf(112, 12)| (Target Name = L"Domain")
// [36-43] - |SecBuf(124, 8)| (User = L"User")
// [44-51] - |SecBuf(132, 16)| (Workstation = L"COMPUTER")
// [52-59] - |SecBuf(64, 0)| (Session Key (empty))
// [60-63] - 0x00088203 (Flags)
// [64-87] - |EXPECTED_V1_WITH_SS_LM_RESPONSE| (LM Response Payload)
// [88-111] - |EXPECTED_V1_WITH_SS_NTLM_RESPONSE| (NTLM Response Payload)
// [112-123]- L"Domain" (Target Name Payload)
// [124-132]- L"User" (User Payload)
// [132-147]- L"COMPUTER" (Workstation Payload)
//
// NOTE: This is not identical to the message in [MS-NLMP] Section 4.2.2.3 for
// several reasons.
//
// 1) The flags are different because this implementation does not support
// the flags related to version, key exchange, signing and sealing. These
// flags are not relevant to implementing the NTLM scheme in HTTP.
// 2) Since key exchange is not required nor supported, the session base key
// payload is not required nor present.
// 3) The specification allows payloads to be in any order. This (and the
// prior) implementation uses a different payload order than the example.
// 4) The version field is Windows specific and there is no provision for
// non-Windows OS information. This message does not include a version field.
constexpr uint8_t kExpectedAuthenticateMsgV1[] = {
'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0', 0x03, 0x00, 0x00, 0x00,
0x18, 0x00, 0x18, 0x00, 0x40, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00,
0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x70, 0x00, 0x00, 0x00,
0x08, 0x00, 0x08, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x03, 0x82, 0x08, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x75, 0x37, 0xf8, 0x03, 0xae, 0x36, 0x71, 0x28,
0xca, 0x45, 0x82, 0x04, 0xbd, 0xe7, 0xca, 0xf8, 0x1e, 0x97, 0xed, 0x26,
0x83, 0x26, 0x72, 0x32, 'D', 0x00, 'o', 0x00, 'm', 0x00, 'a', 0x00,
'i', 0x00, 'n', 0x00, 'U', 0x00, 's', 0x00, 'e', 0x00, 'r', 0x00,
'C', 0x00, 'O', 0x00, 'M', 0x00, 'P', 0x00, 'U', 0x00, 'T', 0x00,
'E', 0x00, 'R', 0x00,
};
} // namespace test
} // namespace ntlm
} // namespace net
......
......@@ -13,6 +13,9 @@
#include "net/ntlm/ntlm.h"
#include <string>
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "net/ntlm/ntlm_test_data.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -117,4 +120,4 @@ TEST(NtlmTest, GenerateResponsesV1WithSessionSecurityVerifySSUsed) {
}
} // namespace ntlm
} // namespace net
\ No newline at end of file
} // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment