| Index: net/http/http_auth_handler_ntlm_portable_unittest.cc
 | 
| diff --git a/net/http/http_auth_handler_ntlm_portable_unittest.cc b/net/http/http_auth_handler_ntlm_portable_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..b14f5f31fbf34ed52c458cd81ecd6c57522bf746
 | 
| --- /dev/null
 | 
| +++ b/net/http/http_auth_handler_ntlm_portable_unittest.cc
 | 
| @@ -0,0 +1,552 @@
 | 
| +// 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/http_auth_handler_ntlm.h"
 | 
| +
 | 
| +#include <string>
 | 
| +
 | 
| +#include "base/base64.h"
 | 
| +#include "base/memory/ptr_util.h"
 | 
| +#include "base/strings/string_util.h"
 | 
| +#include "base/strings/utf_string_conversions.h"
 | 
| +#include "net/base/net_errors.h"
 | 
| +#include "net/base/test_completion_callback.h"
 | 
| +#include "net/dns/mock_host_resolver.h"
 | 
| +#include "net/http/http_auth_challenge_tokenizer.h"
 | 
| +#include "net/http/http_request_info.h"
 | 
| +#include "net/http/mock_allow_http_auth_preferences.h"
 | 
| +#include "net/http/ntlm.h"
 | 
| +#include "net/http/ntlm_buffer_reader.h"
 | 
| +#include "net/http/ntlm_buffer_writer.h"
 | 
| +#include "net/http/ntlm_client.h"
 | 
| +#include "net/log/net_log_with_source.h"
 | 
| +#include "net/ssl/ssl_info.h"
 | 
| +#include "net/test/gtest_util.h"
 | 
| +#include "testing/gmock/include/gmock/gmock.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +#include "testing/platform_test.h"
 | 
| +
 | 
| +using net::test::IsError;
 | 
| +using net::test::IsOk;
 | 
| +
 | 
| +namespace net {
 | 
| +
 | 
| +#if defined(NTLM_PORTABLE)
 | 
| +
 | 
| +class HttpAuthHandlerNtlmPortableTest : public PlatformTest {
 | 
| + public:
 | 
| +  HttpAuthHandlerNtlmPortableTest()
 | 
| +      : domain_ascii_("THEDOMAIN"),
 | 
| +        user_ascii_("someuser"),
 | 
| +        password_ascii_("password") {
 | 
| +    http_auth_preferences_.reset(new MockAllowHttpAuthPreferences());
 | 
| +    factory_.reset(new HttpAuthHandlerNTLM::Factory());
 | 
| +    factory_->set_http_auth_preferences(http_auth_preferences_.get());
 | 
| +    creds_ =
 | 
| +        AuthCredentials(base::ASCIIToUTF16(domain_ascii_ + "\\" + user_ascii_),
 | 
| +                        base::ASCIIToUTF16(password_ascii_));
 | 
| +  }
 | 
| +
 | 
| +  int CreateHandler() {
 | 
| +    GURL gurl("https://foo.com");
 | 
| +    SSLInfo null_ssl_info;
 | 
| +
 | 
| +    return factory_->CreateAuthHandlerFromString(
 | 
| +        "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, gurl, NetLogWithSource(),
 | 
| +        &auth_handler_);
 | 
| +  }
 | 
| +
 | 
| +  std::string CreateType2Token(base::StringPiece message) {
 | 
| +    std::string output;
 | 
| +    base::Base64Encode(message, &output);
 | 
| +
 | 
| +    return "NTLM " + output;
 | 
| +  }
 | 
| +
 | 
| +  void HandleAnotherChallenge(const std::string& challenge,
 | 
| +                              HttpAuth::AuthorizationResult expected_result) {
 | 
| +    HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end());
 | 
| +    EXPECT_EQ(expected_result,
 | 
| +              GetAuthHandler()->HandleAnotherChallenge(&tokenizer));
 | 
| +  }
 | 
| +
 | 
| +  void HandleAnotherChallenge(const std::string& challenge) {
 | 
| +    HandleAnotherChallenge(challenge, HttpAuth::AUTHORIZATION_RESULT_ACCEPT);
 | 
| +  }
 | 
| +
 | 
| +  bool DecodeChallenge(const std::string& challenge, std::string* decoded) {
 | 
| +    HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end());
 | 
| +    return base::Base64Decode(tokenizer.base64_param(), decoded);
 | 
| +  }
 | 
| +
 | 
| +  int GenerateAuthToken(std::string* token) {
 | 
| +    TestCompletionCallback callback;
 | 
| +    HttpRequestInfo request_info;
 | 
| +    return callback.GetResult(GetAuthHandler()->GenerateAuthToken(
 | 
| +        GetCreds(), &request_info, callback.callback(), token));
 | 
| +  }
 | 
| +
 | 
| +  int GetGenerateAuthTokenResult() {
 | 
| +    std::string token;
 | 
| +    return GenerateAuthToken(&token);
 | 
| +  }
 | 
| +
 | 
| +  AuthCredentials* GetCreds() { return &creds_; }
 | 
| +
 | 
| +  HttpAuthHandlerNTLM* GetAuthHandler() {
 | 
| +    return static_cast<HttpAuthHandlerNTLM*>(auth_handler_.get());
 | 
| +  }
 | 
| +
 | 
| +  static void MockRandom(uint8_t* output, size_t n) {
 | 
| +    static const uint8_t bytes[] = {0x55, 0x29, 0x66, 0x26,
 | 
| +                                    0x6b, 0x9c, 0x73, 0x54};
 | 
| +    static size_t current_byte = 0;
 | 
| +    for (size_t i = 0; i < n; ++i) {
 | 
| +      output[i] = bytes[current_byte++];
 | 
| +      current_byte %= arraysize(bytes);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  static std::string MockGetHostName() { return "MYHOSTNAME"; }
 | 
| +
 | 
| + protected:
 | 
| +  const std::string domain_ascii_;
 | 
| +  const std::string user_ascii_;
 | 
| +  const std::string password_ascii_;
 | 
| +
 | 
| + private:
 | 
| +  AuthCredentials creds_;
 | 
| +  std::unique_ptr<HttpAuthHandler> auth_handler_;
 | 
| +  std::unique_ptr<MockAllowHttpAuthPreferences> http_auth_preferences_;
 | 
| +  std::unique_ptr<HttpAuthHandlerNTLM::Factory> factory_;
 | 
| +};
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, SimpleConstruction) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +  ASSERT_TRUE(GetAuthHandler() != nullptr);
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, DoNotAllowDefaultCreds) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +  EXPECT_FALSE(GetAuthHandler()->AllowsDefaultCredentials());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, AllowsExplicitCredentials) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +  EXPECT_TRUE(GetAuthHandler()->AllowsExplicitCredentials());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, VerifyType1Message) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  std::string token;
 | 
| +  EXPECT_EQ(OK, GenerateAuthToken(&token));
 | 
| +  // The type 1 message generated is always the same. The only variable
 | 
| +  // part of the message is the flags and Chrome always offers the same
 | 
| +  // set of flags.
 | 
| +  EXPECT_EQ("NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA=", token);
 | 
| +
 | 
| +  // Poke into the message to verify the fields inside are expected.
 | 
| +  std::string decoded;
 | 
| +  EXPECT_TRUE(DecodeChallenge(token, &decoded));
 | 
| +
 | 
| +  NtlmBufferReader reader(decoded);
 | 
| +  EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MESSAGE_NEGOTIATE));
 | 
| +  uint32_t flags;
 | 
| +  EXPECT_TRUE(reader.ReadUInt32(&flags));
 | 
| +  EXPECT_EQ(ntlm::NEGOTIATE_MESSAGE_FLAGS, flags);
 | 
| +  EXPECT_TRUE(reader.MatchEmptySecurityBuffer());
 | 
| +  EXPECT_TRUE(reader.MatchEmptySecurityBuffer());
 | 
| +  EXPECT_TRUE(reader.IsEndOfBuffer());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, EmptyTokenFails) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // The encoded token for a type 2 message can't be empty.
 | 
| +  HandleAnotherChallenge("NTLM", HttpAuth::AUTHORIZATION_RESULT_REJECT);
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, InvalidBase64Encoding) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Token isn't valid base64.
 | 
| +  HandleAnotherChallenge("NTLM !!!!!!!!!!!!!");
 | 
| +  EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, CantChangeSchemeMidway) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Can't switch to a different auth scheme in the middle of the process.
 | 
| +  HandleAnotherChallenge(
 | 
| +      "Negotiate "
 | 
| +      "TlRMTVNTUAACAAAADAAMADgAAAAFgokCXziKeNIPIDYAAAAAAAAAAIYAhgBEAAAABgOAJQAA"
 | 
| +      "AA9aAEUATgBEAE8ATQACAAwAWgBFAE4ARABPAE0AAQAMAFoARQBOAEQAQwAxAAQAFAB6AGUA"
 | 
| +      "bgBkAG8AbQAuAGwAbwBjAAMAIgBaAGUAbgBEAEMAMQAuAHoAZQBuAGQAbwBtAC4AbABvAGMA"
 | 
| +      "BQAUAHoAZQBuAGQAbwBtAC4AbABvAGMABwAIAN6N+IxGwNIBAAAAAA==",
 | 
| +      HttpAuth::AUTHORIZATION_RESULT_INVALID);
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageTooShort) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Fail because the minimum size valid message is 32 bytes.
 | 
| +  char raw[31];
 | 
| +  HandleAnotherChallenge(CreateType2Token(base::StringPiece(raw, sizeof(raw))));
 | 
| +  EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageNoSig) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Fail because the first bytes don't match "NTLMSSP\0"
 | 
| +  char raw[32];
 | 
| +  memset(raw, 0, ntlm::SIGNATURE_LEN);
 | 
| +  HandleAnotherChallenge(CreateType2Token(base::StringPiece(raw, sizeof(raw))));
 | 
| +  EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type2WrongMessageType) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Fail because the message type should be MESSAGE_CHALLENGE (0x00000002)
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_NEGOTIATE);
 | 
| +
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, MinimalStructurallyValidType2) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +
 | 
| +  // A message with both length and offset equal zero is not forbidden
 | 
| +  // by the spec (2.2.1.2) however it is not what is recommended.
 | 
| +  // But test it anyway.
 | 
| +  writer.WriteEmptySecurityBuffer();
 | 
| +
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithNoTargetName) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +
 | 
| +  // 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.
 | 
| +  writer.WriteSecurityBuffer(ntlm::SecurityBuffer(32, 0));
 | 
| +
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithTargetName) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // One extra byte is provided for target name.
 | 
| +  NtlmBufferWriter writer(33);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +
 | 
| +  // The target name field is 1 byte long.
 | 
| +  writer.WriteSecurityBuffer(ntlm::SecurityBuffer(32, 1));
 | 
| +
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromOffset) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +
 | 
| +  // Claim that the target name field is 1 byte long and outside
 | 
| +  // the buffer.
 | 
| +  writer.WriteSecurityBuffer(ntlm::SecurityBuffer(32, 1));
 | 
| +
 | 
| +  // 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.
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromLength) {
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Message has 1 extra byte of space after the header for the
 | 
| +  // target name.
 | 
| +  NtlmBufferWriter writer(33);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +  // Claim that the target name field is 2 bytes long but
 | 
| +  // there is only 1 byte of space.
 | 
| +  writer.WriteSecurityBuffer(ntlm::SecurityBuffer(32, 2));
 | 
| +
 | 
| +  // 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.
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult());
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type3RespectsUnicode) {
 | 
| +  HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom,
 | 
| +                                                    MockGetHostName);
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Generate the type 2 message from the server.
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +  // No target name. Chrome doesn't use it anyway.
 | 
| +  writer.WriteEmptySecurityBuffer();
 | 
| +  // Set the unicode flag.
 | 
| +  writer.WriteUInt32(ntlm::NTLMSSP_NEGOTIATE_UNICODE);
 | 
| +
 | 
| +  std::string token;
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GenerateAuthToken(&token));
 | 
| +
 | 
| +  // Validate the type 3 message
 | 
| +  std::string decoded;
 | 
| +  EXPECT_TRUE(DecodeChallenge(token, &decoded));
 | 
| +  NtlmBufferReader reader(decoded);
 | 
| +  EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MESSAGE_AUTHENTICATE));
 | 
| +
 | 
| +  // Skip the LM and NTLM Hash fields. This test isn't testing that.
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBuffer());
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBuffer());
 | 
| +  base::string16 domain;
 | 
| +  base::string16 username;
 | 
| +  base::string16 hostname;
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&domain));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(domain_ascii_), domain);
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&username));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(user_ascii_), username);
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&hostname));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(MockGetHostName()), hostname);
 | 
| +
 | 
| +  // Skip the session key which isn't used.
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBufferWithValidation());
 | 
| +
 | 
| +  // Verify the unicode flag is set.
 | 
| +  uint32_t flags;
 | 
| +  EXPECT_TRUE(reader.ReadUInt32(&flags));
 | 
| +  EXPECT_EQ(ntlm::NTLMSSP_NEGOTIATE_UNICODE,
 | 
| +            flags & ntlm::NTLMSSP_NEGOTIATE_UNICODE);
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type3WithoutUnicode) {
 | 
| +  HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom,
 | 
| +                                                    MockGetHostName);
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Generate the type 2 message from the server.
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE);
 | 
| +  // No target name. Chrome doesn't use it anyway.
 | 
| +  writer.WriteEmptySecurityBuffer();
 | 
| +  // Set the OEM flag.
 | 
| +  writer.WriteUInt32(ntlm::NTLMSSP_NEGOTIATE_OEM);
 | 
| +
 | 
| +  std::string token;
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GenerateAuthToken(&token));
 | 
| +
 | 
| +  // Validate the type 3 message
 | 
| +  std::string decoded;
 | 
| +  EXPECT_TRUE(DecodeChallenge(token, &decoded));
 | 
| +  NtlmBufferReader reader(decoded);
 | 
| +  EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MESSAGE_AUTHENTICATE));
 | 
| +
 | 
| +  // Skip the 2 hash fields. This test isn't testing that.
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBufferWithValidation());
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBufferWithValidation());
 | 
| +  std::string domain;
 | 
| +  std::string username;
 | 
| +  std::string hostname;
 | 
| +  EXPECT_TRUE(reader.ReadAsciiPayload(&domain));
 | 
| +  EXPECT_EQ(domain_ascii_, domain);
 | 
| +  EXPECT_TRUE(reader.ReadAsciiPayload(&username));
 | 
| +  EXPECT_EQ(user_ascii_, username);
 | 
| +  EXPECT_TRUE(reader.ReadAsciiPayload(&hostname));
 | 
| +  EXPECT_EQ(MockGetHostName(), hostname);
 | 
| +
 | 
| +  // Skip the session key which isn't used.
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBufferWithValidation());
 | 
| +
 | 
| +  // Verify the unicode flag is not set and OEM flag is.
 | 
| +  uint32_t flags;
 | 
| +  EXPECT_TRUE(reader.ReadUInt32(&flags));
 | 
| +  EXPECT_EQ(0u, flags & ntlm::NTLMSSP_NEGOTIATE_UNICODE);
 | 
| +  EXPECT_EQ(ntlm::NTLMSSP_NEGOTIATE_OEM, flags & ntlm::NTLMSSP_NEGOTIATE_OEM);
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type3UnicodeNoSessionSecurity) {
 | 
| +  // Verify that the client won't be downgraded if the server clears
 | 
| +  // the session security flag.
 | 
| +  HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom,
 | 
| +                                                    MockGetHostName);
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Generate the type 2 message from the server.
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE));
 | 
| +  // No target name. Chrome doesn't use it anyway.
 | 
| +  EXPECT_TRUE(writer.WriteEmptySecurityBuffer());
 | 
| +  // Set the unicode but not the session security flag.
 | 
| +  EXPECT_TRUE(writer.WriteUInt32(ntlm::NTLMSSP_NEGOTIATE_UNICODE));
 | 
| +
 | 
| +  uint8_t server_challenge[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
 | 
| +  EXPECT_EQ(arraysize(server_challenge), ntlm::CHALLENGE_LEN);
 | 
| +  EXPECT_TRUE(writer.WriteBytes(server_challenge, ntlm::CHALLENGE_LEN));
 | 
| +  EXPECT_TRUE(writer.IsEndOfBuffer());
 | 
| +
 | 
| +  std::string token;
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GenerateAuthToken(&token));
 | 
| +
 | 
| +  // Validate the type 3 message
 | 
| +  std::string decoded;
 | 
| +  EXPECT_TRUE(DecodeChallenge(token, &decoded));
 | 
| +  NtlmBufferReader reader(decoded);
 | 
| +  EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MESSAGE_AUTHENTICATE));
 | 
| +
 | 
| +  // Read the LM and NTLM Response Payloads.
 | 
| +  uint8_t expected_lm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +  uint8_t expected_ntlm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +  uint8_t actual_lm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +  uint8_t actual_ntlm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +
 | 
| +  EXPECT_TRUE(
 | 
| +      reader.ReadBytesPayload(actual_lm_response, ntlm::RESPONSE_V1_LEN));
 | 
| +  EXPECT_TRUE(
 | 
| +      reader.ReadBytesPayload(actual_ntlm_response, ntlm::RESPONSE_V1_LEN));
 | 
| +
 | 
| +  // Session security also uses a client generated challenge so
 | 
| +  // use the mock to get the same value that the implementation
 | 
| +  // would get.
 | 
| +  uint8_t client_challenge[ntlm::CHALLENGE_LEN];
 | 
| +  MockRandom(client_challenge, ntlm::CHALLENGE_LEN);
 | 
| +
 | 
| +  ntlm::GenerateResponsesV1WithSS(base::ASCIIToUTF16(password_ascii_),
 | 
| +                                  server_challenge, client_challenge,
 | 
| +                                  expected_lm_response, expected_ntlm_response);
 | 
| +
 | 
| +  // Verify that the client still generated a response that uses
 | 
| +  // session security.
 | 
| +  EXPECT_EQ(0, memcmp(expected_lm_response, actual_lm_response,
 | 
| +                      ntlm::RESPONSE_V1_LEN));
 | 
| +  EXPECT_EQ(0, memcmp(expected_ntlm_response, actual_ntlm_response,
 | 
| +                      ntlm::RESPONSE_V1_LEN));
 | 
| +
 | 
| +  base::string16 domain;
 | 
| +  base::string16 username;
 | 
| +  base::string16 hostname;
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&domain));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(domain_ascii_), domain);
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&username));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(user_ascii_), username);
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&hostname));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(MockGetHostName()), hostname);
 | 
| +
 | 
| +  // Skip the session key which isn't used.
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBufferWithValidation());
 | 
| +
 | 
| +  // Verify the unicode flag is set.
 | 
| +  uint32_t flags;
 | 
| +  EXPECT_TRUE(reader.ReadUInt32(&flags));
 | 
| +  EXPECT_EQ(ntlm::NTLMSSP_NEGOTIATE_UNICODE,
 | 
| +            flags & ntlm::NTLMSSP_NEGOTIATE_UNICODE);
 | 
| +}
 | 
| +
 | 
| +TEST_F(HttpAuthHandlerNtlmPortableTest, Type3UnicodeWithSessionSecurity) {
 | 
| +  HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom,
 | 
| +                                                    MockGetHostName);
 | 
| +  EXPECT_EQ(OK, CreateHandler());
 | 
| +
 | 
| +  // Generate the type 2 message from the server.
 | 
| +  NtlmBufferWriter writer(32);
 | 
| +  EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MESSAGE_CHALLENGE));
 | 
| +  // No target name. Chrome doesn't use it anyway.
 | 
| +  EXPECT_TRUE(writer.WriteEmptySecurityBuffer());
 | 
| +  // Set the unicode and session security flag.
 | 
| +  EXPECT_TRUE(
 | 
| +      writer.WriteUInt32(ntlm::NTLMSSP_NEGOTIATE_UNICODE |
 | 
| +                         ntlm::NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY));
 | 
| +
 | 
| +  uint8_t server_challenge[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
 | 
| +  EXPECT_EQ(arraysize(server_challenge), ntlm::CHALLENGE_LEN);
 | 
| +  EXPECT_TRUE(writer.WriteBytes(server_challenge, ntlm::CHALLENGE_LEN));
 | 
| +  EXPECT_TRUE(writer.IsEndOfBuffer());
 | 
| +
 | 
| +  std::string token;
 | 
| +  HandleAnotherChallenge(CreateType2Token(writer.GetBuffer()));
 | 
| +  EXPECT_EQ(OK, GenerateAuthToken(&token));
 | 
| +
 | 
| +  // Validate the type 3 message
 | 
| +  std::string decoded;
 | 
| +  EXPECT_TRUE(DecodeChallenge(token, &decoded));
 | 
| +  NtlmBufferReader reader(decoded);
 | 
| +  EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MESSAGE_AUTHENTICATE));
 | 
| +
 | 
| +  // Read the LM and NTLM Response Payloads.
 | 
| +  uint8_t expected_lm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +  uint8_t expected_ntlm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +  uint8_t actual_lm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +  uint8_t actual_ntlm_response[ntlm::RESPONSE_V1_LEN];
 | 
| +
 | 
| +  EXPECT_TRUE(
 | 
| +      reader.ReadBytesPayload(actual_lm_response, ntlm::RESPONSE_V1_LEN));
 | 
| +  EXPECT_TRUE(
 | 
| +      reader.ReadBytesPayload(actual_ntlm_response, ntlm::RESPONSE_V1_LEN));
 | 
| +
 | 
| +  // Session security also uses a client generated challenge so
 | 
| +  // use the mock to get the same value that the implementation
 | 
| +  // would get.
 | 
| +  uint8_t client_challenge[ntlm::CHALLENGE_LEN];
 | 
| +  MockRandom(client_challenge, ntlm::CHALLENGE_LEN);
 | 
| +
 | 
| +  ntlm::GenerateResponsesV1WithSS(base::ASCIIToUTF16(password_ascii_),
 | 
| +                                  server_challenge, client_challenge,
 | 
| +                                  expected_lm_response, expected_ntlm_response);
 | 
| +
 | 
| +  EXPECT_EQ(0, memcmp(expected_lm_response, actual_lm_response,
 | 
| +                      ntlm::RESPONSE_V1_LEN));
 | 
| +  EXPECT_EQ(0, memcmp(expected_ntlm_response, actual_ntlm_response,
 | 
| +                      ntlm::RESPONSE_V1_LEN));
 | 
| +
 | 
| +  base::string16 domain;
 | 
| +  base::string16 username;
 | 
| +  base::string16 hostname;
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&domain));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(domain_ascii_), domain);
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&username));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(user_ascii_), username);
 | 
| +  EXPECT_TRUE(reader.ReadUnicodePayload(&hostname));
 | 
| +  EXPECT_EQ(base::ASCIIToUTF16(MockGetHostName()), hostname);
 | 
| +
 | 
| +  // Skip the session key which isn't used.
 | 
| +  EXPECT_TRUE(reader.SkipSecurityBufferWithValidation());
 | 
| +
 | 
| +  // Verify the unicode flag is set.
 | 
| +  uint32_t flags;
 | 
| +  EXPECT_TRUE(reader.ReadUInt32(&flags));
 | 
| +  EXPECT_EQ(ntlm::NTLMSSP_NEGOTIATE_UNICODE,
 | 
| +            flags & ntlm::NTLMSSP_NEGOTIATE_UNICODE);
 | 
| +}
 | 
| +
 | 
| +#endif  // defined(NTLM_PORTABLE)
 | 
| +
 | 
| +}  // namespace net
 | 
| 
 |