• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
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 <memory>
6 #include <string>
7 
8 #include "base/base64.h"
9 #include "base/containers/span.h"
10 #include "base/strings/strcat.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "net/base/network_anonymization_key.h"
15 #include "net/base/test_completion_callback.h"
16 #include "net/dns/mock_host_resolver.h"
17 #include "net/http/http_auth_challenge_tokenizer.h"
18 #include "net/http/http_auth_handler_ntlm.h"
19 #include "net/http/http_auth_ntlm_mechanism.h"
20 #include "net/http/http_request_info.h"
21 #include "net/http/mock_allow_http_auth_preferences.h"
22 #include "net/log/net_log_with_source.h"
23 #include "net/ntlm/ntlm.h"
24 #include "net/ntlm/ntlm_buffer_reader.h"
25 #include "net/ntlm/ntlm_buffer_writer.h"
26 #include "net/ntlm/ntlm_test_data.h"
27 #include "net/ssl/ssl_info.h"
28 #include "net/test/gtest_util.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "testing/platform_test.h"
32 #include "url/gurl.h"
33 #include "url/scheme_host_port.h"
34 
35 namespace net {
36 
37 class HttpAuthHandlerNtlmPortableTest : public PlatformTest {
38  public:
39   // Test input value defined in [MS-NLMP] Section 4.2.1.
HttpAuthHandlerNtlmPortableTest()40   HttpAuthHandlerNtlmPortableTest() {
41     http_auth_preferences_ = std::make_unique<MockAllowHttpAuthPreferences>();
42     // Disable NTLMv2 for this end to end test because it's not possible
43     // to mock all the required dependencies for NTLMv2 from here. These
44     // tests are only of the overall flow, and the detailed tests of the
45     // contents of the protocol messages are in ntlm_client_unittest.cc
46     http_auth_preferences_->set_ntlm_v2_enabled(false);
47     factory_ = std::make_unique<HttpAuthHandlerNTLM::Factory>();
48     factory_->set_http_auth_preferences(http_auth_preferences_.get());
49     creds_ = AuthCredentials(
50         base::StrCat({ntlm::test::kNtlmDomain, u"\\", ntlm::test::kUser}),
51         ntlm::test::kPassword);
52   }
53 
CreateHandler()54   int CreateHandler() {
55     url::SchemeHostPort scheme_host_port(GURL("https://foo.com"));
56     SSLInfo null_ssl_info;
57 
58     return factory_->CreateAuthHandlerFromString(
59         "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, NetworkAnonymizationKey(),
60         scheme_host_port, NetLogWithSource(), nullptr, &auth_handler_);
61   }
62 
CreateNtlmAuthHeader(base::span<const uint8_t> buffer)63   std::string CreateNtlmAuthHeader(base::span<const uint8_t> buffer) {
64     std::string output;
65     base::Base64Encode(
66         base::StringPiece(reinterpret_cast<const char*>(buffer.data()),
67                           buffer.size()),
68         &output);
69 
70     return "NTLM " + output;
71   }
72 
73 
HandleAnotherChallenge(const std::string & challenge)74   HttpAuth::AuthorizationResult HandleAnotherChallenge(
75       const std::string& challenge) {
76     HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end());
77     return GetAuthHandler()->HandleAnotherChallenge(&tokenizer);
78   }
79 
DecodeChallenge(const std::string & challenge,std::string * decoded)80   bool DecodeChallenge(const std::string& challenge, std::string* decoded) {
81     HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end());
82     return base::Base64Decode(tokenizer.base64_param(), decoded);
83   }
84 
GenerateAuthToken(std::string * token)85   int GenerateAuthToken(std::string* token) {
86     TestCompletionCallback callback;
87     HttpRequestInfo request_info;
88     return callback.GetResult(GetAuthHandler()->GenerateAuthToken(
89         GetCreds(), &request_info, callback.callback(), token));
90   }
91 
ReadBytesPayload(ntlm::NtlmBufferReader * reader,base::span<uint8_t> buffer)92   bool ReadBytesPayload(ntlm::NtlmBufferReader* reader,
93                         base::span<uint8_t> buffer) {
94     ntlm::SecurityBuffer sec_buf;
95     return reader->ReadSecurityBuffer(&sec_buf) &&
96            (sec_buf.length == buffer.size()) &&
97            reader->ReadBytesFrom(sec_buf, buffer);
98   }
99 
100   // Reads bytes from a payload and assigns them to a string. This makes
101   // no assumptions about the underlying encoding.
ReadStringPayload(ntlm::NtlmBufferReader * reader,std::string * str)102   bool ReadStringPayload(ntlm::NtlmBufferReader* reader, std::string* str) {
103     ntlm::SecurityBuffer sec_buf;
104     if (!reader->ReadSecurityBuffer(&sec_buf))
105       return false;
106 
107     if (!reader->ReadBytesFrom(
108             sec_buf,
109             base::as_writable_bytes(base::make_span(
110                 base::WriteInto(str, sec_buf.length + 1), sec_buf.length)))) {
111       return false;
112     }
113 
114     return true;
115   }
116 
117   // Reads bytes from a payload and assigns them to a string16. This makes
118   // no assumptions about the underlying encoding. This will fail if there
119   // are an odd number of bytes in the payload.
ReadString16Payload(ntlm::NtlmBufferReader * reader,std::u16string * str)120   void ReadString16Payload(ntlm::NtlmBufferReader* reader,
121                            std::u16string* str) {
122     ntlm::SecurityBuffer sec_buf;
123     EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf));
124     EXPECT_EQ(0, sec_buf.length % 2);
125 
126     std::vector<uint8_t> raw(sec_buf.length);
127     EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, raw));
128 
129 #if defined(ARCH_CPU_BIG_ENDIAN)
130     for (size_t i = 0; i < raw.size(); i += 2) {
131       std::swap(raw[i], raw[i + 1]);
132     }
133 #endif
134 
135     str->assign(reinterpret_cast<const char16_t*>(raw.data()), raw.size() / 2);
136   }
137 
GetGenerateAuthTokenResult()138   int GetGenerateAuthTokenResult() {
139     std::string token;
140     return GenerateAuthToken(&token);
141   }
142 
GetCreds()143   AuthCredentials* GetCreds() { return &creds_; }
144 
GetAuthHandler()145   HttpAuthHandlerNTLM* GetAuthHandler() {
146     return static_cast<HttpAuthHandlerNTLM*>(auth_handler_.get());
147   }
148 
MockRandom(uint8_t * output,size_t n)149   static void MockRandom(uint8_t* output, size_t n) {
150     // This is set to 0xaa because the client challenge for testing in
151     // [MS-NLMP] Section 4.2.1 is 8 bytes of 0xaa.
152     memset(output, 0xaa, n);
153   }
154 
MockGetMSTime()155   static uint64_t MockGetMSTime() {
156     // Tue, 23 May 2017 20:13:07 +0000
157     return 131400439870000000;
158   }
159 
MockGetHostName()160   static std::string MockGetHostName() { return ntlm::test::kHostnameAscii; }
161 
162  private:
163   AuthCredentials creds_;
164   std::unique_ptr<HttpAuthHandler> auth_handler_;
165   std::unique_ptr<MockAllowHttpAuthPreferences> http_auth_preferences_;
166   std::unique_ptr<HttpAuthHandlerNTLM::Factory> factory_;
167 };
168 
TEST_F(HttpAuthHandlerNtlmPortableTest,SimpleConstruction)169 TEST_F(HttpAuthHandlerNtlmPortableTest, SimpleConstruction) {
170   ASSERT_EQ(OK, CreateHandler());
171   ASSERT_TRUE(GetAuthHandler() != nullptr);
172 }
173 
TEST_F(HttpAuthHandlerNtlmPortableTest,DoNotAllowDefaultCreds)174 TEST_F(HttpAuthHandlerNtlmPortableTest, DoNotAllowDefaultCreds) {
175   ASSERT_EQ(OK, CreateHandler());
176   ASSERT_FALSE(GetAuthHandler()->AllowsDefaultCredentials());
177 }
178 
TEST_F(HttpAuthHandlerNtlmPortableTest,AllowsExplicitCredentials)179 TEST_F(HttpAuthHandlerNtlmPortableTest, AllowsExplicitCredentials) {
180   ASSERT_EQ(OK, CreateHandler());
181   ASSERT_TRUE(GetAuthHandler()->AllowsExplicitCredentials());
182 }
183 
TEST_F(HttpAuthHandlerNtlmPortableTest,VerifyType1Message)184 TEST_F(HttpAuthHandlerNtlmPortableTest, VerifyType1Message) {
185   ASSERT_EQ(OK, CreateHandler());
186 
187   std::string token;
188   ASSERT_EQ(OK, GenerateAuthToken(&token));
189   // The type 1 message generated is always the same. The only variable
190   // part of the message is the flags and this implementation always offers
191   // the same set of flags.
192   ASSERT_EQ("NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAgAAAAAAAAACAAAAA=", token);
193 }
194 
TEST_F(HttpAuthHandlerNtlmPortableTest,EmptyTokenFails)195 TEST_F(HttpAuthHandlerNtlmPortableTest, EmptyTokenFails) {
196   ASSERT_EQ(OK, CreateHandler());
197   ASSERT_EQ(OK, GetGenerateAuthTokenResult());
198 
199   // The encoded token for a type 2 message can't be empty.
200   ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
201             HandleAnotherChallenge("NTLM"));
202 }
203 
TEST_F(HttpAuthHandlerNtlmPortableTest,InvalidBase64Encoding)204 TEST_F(HttpAuthHandlerNtlmPortableTest, InvalidBase64Encoding) {
205   ASSERT_EQ(OK, CreateHandler());
206   ASSERT_EQ(OK, GetGenerateAuthTokenResult());
207 
208   // Token isn't valid base64.
209   ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
210             HandleAnotherChallenge("NTLM !!!!!!!!!!!!!"));
211 }
212 
TEST_F(HttpAuthHandlerNtlmPortableTest,CantChangeSchemeMidway)213 TEST_F(HttpAuthHandlerNtlmPortableTest, CantChangeSchemeMidway) {
214   ASSERT_EQ(OK, CreateHandler());
215   ASSERT_EQ(OK, GetGenerateAuthTokenResult());
216 
217   // Can't switch to a different auth scheme in the middle of the process.
218   ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID,
219             HandleAnotherChallenge("Negotiate SSdtIG5vdCBhIHJlYWwgdG9rZW4h"));
220 }
221 
TEST_F(HttpAuthHandlerNtlmPortableTest,NtlmV1AuthenticationSuccess)222 TEST_F(HttpAuthHandlerNtlmPortableTest, NtlmV1AuthenticationSuccess) {
223   HttpAuthNtlmMechanism::ScopedProcSetter proc_setter(MockGetMSTime, MockRandom,
224                                                       MockGetHostName);
225   ASSERT_EQ(OK, CreateHandler());
226   ASSERT_EQ(OK, GetGenerateAuthTokenResult());
227 
228   std::string token;
229   ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT,
230             HandleAnotherChallenge(
231                 CreateNtlmAuthHeader(ntlm::test::kChallengeMsgV1)));
232   ASSERT_EQ(OK, GenerateAuthToken(&token));
233 
234   // Validate the authenticate message
235   std::string decoded;
236   ASSERT_TRUE(DecodeChallenge(token, &decoded));
237   ASSERT_EQ(std::size(ntlm::test::kExpectedAuthenticateMsgSpecResponseV1),
238             decoded.size());
239   ASSERT_EQ(0, memcmp(decoded.data(),
240                       ntlm::test::kExpectedAuthenticateMsgSpecResponseV1,
241                       decoded.size()));
242 }
243 
244 }  // namespace net
245