OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | |
asanka
2017/06/23 21:29:10
No "(c)"
zentaro
2017/07/05 17:57:42
Done.
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/http/http_auth_handler_ntlm.h" | |
asanka
2017/06/23 21:29:10
The primary include file should http_auth_handler_
zentaro
2017/07/05 17:57:41
There is no http_auth_handler_ntlm_portable.h so I
| |
6 | |
7 #include <string> | |
8 | |
9 #include "base/base64.h" | |
10 #include "base/memory/ptr_util.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "net/base/test_completion_callback.h" | |
14 #include "net/dns/mock_host_resolver.h" | |
15 #include "net/http/http_auth_challenge_tokenizer.h" | |
16 #include "net/http/http_request_info.h" | |
17 #include "net/http/mock_allow_http_auth_preferences.h" | |
18 #include "net/http/ntlm.h" | |
19 #include "net/http/ntlm_buffer_reader.h" | |
20 #include "net/http/ntlm_buffer_writer.h" | |
21 #include "net/http/ntlm_client.h" | |
22 #include "net/log/net_log_with_source.h" | |
23 #include "net/ssl/ssl_info.h" | |
24 #include "net/test/gtest_util.h" | |
25 #include "testing/gmock/include/gmock/gmock.h" | |
26 #include "testing/gtest/include/gtest/gtest.h" | |
27 #include "testing/platform_test.h" | |
28 | |
29 namespace net { | |
30 | |
31 #if defined(NTLM_PORTABLE) | |
asanka
2017/06/23 21:29:10
This condition is not necessary and not correct.
zentaro
2017/07/05 17:57:42
Done.
| |
32 | |
33 class HttpAuthHandlerNtlmPortableTest : public PlatformTest { | |
34 public: | |
35 HttpAuthHandlerNtlmPortableTest() | |
36 : domain_ascii_("THEDOMAIN"), | |
37 user_ascii_("someuser"), | |
38 password_ascii_("password") { | |
39 http_auth_preferences_.reset(new MockAllowHttpAuthPreferences()); | |
40 factory_.reset(new HttpAuthHandlerNTLM::Factory()); | |
41 factory_->set_http_auth_preferences(http_auth_preferences_.get()); | |
42 creds_ = | |
43 AuthCredentials(base::ASCIIToUTF16(domain_ascii_ + "\\" + user_ascii_), | |
44 base::ASCIIToUTF16(password_ascii_)); | |
45 } | |
46 | |
47 int CreateHandler() { | |
48 GURL gurl("https://foo.com"); | |
49 SSLInfo null_ssl_info; | |
50 | |
51 return factory_->CreateAuthHandlerFromString( | |
52 "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, gurl, NetLogWithSource(), | |
53 &auth_handler_); | |
54 } | |
55 | |
56 std::string CreateType2Token(base::StringPiece message) { | |
asanka
2017/06/23 21:29:10
Nit: doesn't really create a type2 token. Probably
zentaro
2017/07/05 17:57:41
Done.
| |
57 std::string output; | |
58 base::Base64Encode(message, &output); | |
59 | |
60 return "NTLM " + output; | |
61 } | |
62 | |
63 void HandleAnotherChallenge(const std::string& challenge, | |
asanka
2017/06/23 21:29:10
It's easier to read, even though a bit verbose, if
zentaro
2017/07/05 17:57:42
Done.
| |
64 HttpAuth::AuthorizationResult expected_result) { | |
65 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
66 EXPECT_EQ(expected_result, | |
67 GetAuthHandler()->HandleAnotherChallenge(&tokenizer)); | |
68 } | |
69 | |
70 void HandleAnotherChallenge(const std::string& challenge) { | |
71 HandleAnotherChallenge(challenge, HttpAuth::AUTHORIZATION_RESULT_ACCEPT); | |
72 } | |
73 | |
74 bool DecodeChallenge(const std::string& challenge, std::string* decoded) { | |
75 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
76 return base::Base64Decode(tokenizer.base64_param(), decoded); | |
77 } | |
78 | |
79 int GenerateAuthToken(std::string* token) { | |
80 TestCompletionCallback callback; | |
81 HttpRequestInfo request_info; | |
82 return callback.GetResult(GetAuthHandler()->GenerateAuthToken( | |
83 GetCreds(), &request_info, callback.callback(), token)); | |
84 } | |
85 | |
86 void ReadBytesPayload(ntlm::NtlmBufferReader* reader, | |
87 uint8_t* buffer, | |
88 size_t len) { | |
89 // First read the security buffer. | |
90 ntlm::SecurityBuffer sec_buf; | |
91 EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf)); | |
92 EXPECT_EQ(sec_buf.length, len); | |
asanka
2017/06/23 21:29:10
Should return early if this winds up not being the
zentaro
2017/07/05 17:57:41
When I wrote most of the tests I didn't realize th
| |
93 EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, buffer)); | |
94 } | |
95 | |
96 // Reads bytes from a payload and assigns them to a string. This makes | |
97 // no assumptions about the underlying encoding. | |
98 void ReadStringPayload(ntlm::NtlmBufferReader* reader, std::string* str) { | |
99 ntlm::SecurityBuffer sec_buf; | |
100 EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf)); | |
101 | |
102 uint8_t raw[sec_buf.length]; | |
103 EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, raw)); | |
104 | |
105 str->assign(reinterpret_cast<const char*>(raw), sec_buf.length); | |
106 } | |
107 | |
108 // Reads bytes from a payload and assigns them to a string16. This makes | |
109 // no assumptions about the underlying encoding. This will fail if there | |
110 // are an odd number of bytes in the payload. | |
111 void ReadString16Payload(ntlm::NtlmBufferReader* reader, | |
112 base::string16* str) { | |
113 ntlm::SecurityBuffer sec_buf; | |
114 EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf)); | |
115 EXPECT_EQ(0, sec_buf.length % 2); | |
116 | |
117 uint8_t raw[sec_buf.length]; | |
118 EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, raw)); | |
119 | |
120 #if IS_BIG_ENDIAN | |
121 for (size_t i = 0; i < sec_buf.length; i += 2) { | |
122 std::swap(raw[i], raw[i + 1]); | |
123 } | |
124 #endif | |
125 | |
126 str->assign(reinterpret_cast<const base::char16*>(raw), sec_buf.length / 2); | |
127 } | |
128 | |
129 int GetGenerateAuthTokenResult() { | |
130 std::string token; | |
131 return GenerateAuthToken(&token); | |
132 } | |
133 | |
134 AuthCredentials* GetCreds() { return &creds_; } | |
135 | |
136 HttpAuthHandlerNTLM* GetAuthHandler() { | |
137 return static_cast<HttpAuthHandlerNTLM*>(auth_handler_.get()); | |
138 } | |
139 | |
140 static void MockRandom(uint8_t* output, size_t n) { | |
asanka
2017/06/23 21:29:10
Why not memset(output, 4, n) ? https://xkcd.com/22
zentaro
2017/07/05 17:57:41
Works for me. I copy/pasted it from net/http/http_
| |
141 static const uint8_t bytes[] = {0x55, 0x29, 0x66, 0x26, | |
142 0x6b, 0x9c, 0x73, 0x54}; | |
143 static size_t current_byte = 0; | |
144 for (size_t i = 0; i < n; ++i) { | |
145 output[i] = bytes[current_byte++]; | |
146 current_byte %= arraysize(bytes); | |
147 } | |
148 } | |
149 | |
150 static std::string MockGetHostName() { return "MYHOSTNAME"; } | |
151 | |
152 protected: | |
153 const std::string domain_ascii_; | |
154 const std::string user_ascii_; | |
155 const std::string password_ascii_; | |
156 | |
157 private: | |
158 AuthCredentials creds_; | |
159 std::unique_ptr<HttpAuthHandler> auth_handler_; | |
160 std::unique_ptr<MockAllowHttpAuthPreferences> http_auth_preferences_; | |
161 std::unique_ptr<HttpAuthHandlerNTLM::Factory> factory_; | |
162 }; | |
163 | |
164 TEST_F(HttpAuthHandlerNtlmPortableTest, SimpleConstruction) { | |
165 EXPECT_EQ(OK, CreateHandler()); | |
166 ASSERT_TRUE(GetAuthHandler() != nullptr); | |
167 } | |
168 | |
169 TEST_F(HttpAuthHandlerNtlmPortableTest, DoNotAllowDefaultCreds) { | |
170 EXPECT_EQ(OK, CreateHandler()); | |
171 EXPECT_FALSE(GetAuthHandler()->AllowsDefaultCredentials()); | |
172 } | |
173 | |
174 TEST_F(HttpAuthHandlerNtlmPortableTest, AllowsExplicitCredentials) { | |
175 EXPECT_EQ(OK, CreateHandler()); | |
176 EXPECT_TRUE(GetAuthHandler()->AllowsExplicitCredentials()); | |
177 } | |
178 | |
179 TEST_F(HttpAuthHandlerNtlmPortableTest, VerifyType1Message) { | |
180 EXPECT_EQ(OK, CreateHandler()); | |
181 | |
182 std::string token; | |
183 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
184 // The type 1 message generated is always the same. The only variable | |
185 // part of the message is the flags and Chrome always offers the same | |
186 // set of flags. | |
187 EXPECT_EQ("NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA=", token); | |
188 | |
asanka
2017/06/23 21:29:10
The remainder of the test is basically testing the
zentaro
2017/07/05 17:57:41
Done.
| |
189 // Poke into the message to verify the fields inside are expected. | |
190 std::string decoded; | |
191 EXPECT_TRUE(DecodeChallenge(token, &decoded)); | |
192 | |
193 ntlm::NtlmBufferReader reader(decoded); | |
194 EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::NEGOTIATE)); | |
195 ntlm::NegotiateFlags flags; | |
196 EXPECT_TRUE(reader.ReadFlags(&flags)); | |
197 EXPECT_EQ(ntlm::NEGOTIATE_MESSAGE_FLAGS, flags); | |
198 EXPECT_TRUE(reader.MatchEmptySecurityBuffer()); | |
199 EXPECT_TRUE(reader.MatchEmptySecurityBuffer()); | |
200 EXPECT_TRUE(reader.IsEndOfBuffer()); | |
201 } | |
202 | |
203 TEST_F(HttpAuthHandlerNtlmPortableTest, EmptyTokenFails) { | |
204 EXPECT_EQ(OK, CreateHandler()); | |
205 | |
206 // The encoded token for a type 2 message can't be empty. | |
207 HandleAnotherChallenge("NTLM", HttpAuth::AUTHORIZATION_RESULT_REJECT); | |
208 } | |
209 | |
210 TEST_F(HttpAuthHandlerNtlmPortableTest, InvalidBase64Encoding) { | |
211 EXPECT_EQ(OK, CreateHandler()); | |
212 | |
213 // Token isn't valid base64. | |
214 HandleAnotherChallenge("NTLM !!!!!!!!!!!!!"); | |
215 EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
216 } | |
217 | |
218 TEST_F(HttpAuthHandlerNtlmPortableTest, CantChangeSchemeMidway) { | |
219 EXPECT_EQ(OK, CreateHandler()); | |
220 | |
221 // Can't switch to a different auth scheme in the middle of the process. | |
222 HandleAnotherChallenge( | |
223 "Negotiate " | |
224 "TlRMTVNTUAACAAAADAAMADgAAAAFgokCXziKeNIPIDYAAAAAAAAAAIYAhgBEAAAABgOAJQAA" | |
asanka
2017/06/23 21:29:10
The token is irrelevant. Let's go with something s
zentaro
2017/07/05 17:57:41
Done.
| |
225 "AA9aAEUATgBEAE8ATQACAAwAWgBFAE4ARABPAE0AAQAMAFoARQBOAEQAQwAxAAQAFAB6AGUA" | |
226 "bgBkAG8AbQAuAGwAbwBjAAMAIgBaAGUAbgBEAEMAMQAuAHoAZQBuAGQAbwBtAC4AbABvAGMA" | |
227 "BQAUAHoAZQBuAGQAbwBtAC4AbABvAGMABwAIAN6N+IxGwNIBAAAAAA==", | |
228 HttpAuth::AUTHORIZATION_RESULT_INVALID); | |
229 } | |
230 | |
231 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageTooShort) { | |
232 EXPECT_EQ(OK, CreateHandler()); | |
233 | |
234 // Fail because the minimum size valid message is 32 bytes. | |
235 char raw[31]; | |
236 HandleAnotherChallenge(CreateType2Token(base::StringPiece(raw, sizeof(raw)))); | |
asanka
2017/06/23 21:29:10
The memory bots may complain about this because th
zentaro
2017/07/05 17:57:41
Done.
| |
237 EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
238 } | |
239 | |
240 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageNoSig) { | |
241 EXPECT_EQ(OK, CreateHandler()); | |
242 | |
243 // Fail because the first bytes don't match "NTLMSSP\0" | |
244 char raw[32]; | |
asanka
2017/06/23 21:29:11
Same as before. Use a known initialized buffer for
zentaro
2017/07/05 17:57:41
Done.
| |
245 memset(raw, 0, ntlm::SIGNATURE_LEN); | |
246 HandleAnotherChallenge(CreateType2Token(base::StringPiece(raw, sizeof(raw)))); | |
247 EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
248 } | |
249 | |
250 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2WrongMessageType) { | |
251 EXPECT_EQ(OK, CreateHandler()); | |
252 | |
253 // Fail because the message type should be MessageType::CHALLENGE (0x00000002) | |
254 ntlm::NtlmBufferWriter writer(32); | |
255 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::NEGOTIATE)); | |
256 | |
257 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
258 EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
259 } | |
260 | |
261 TEST_F(HttpAuthHandlerNtlmPortableTest, MinimalStructurallyValidType2) { | |
262 EXPECT_EQ(OK, CreateHandler()); | |
263 | |
264 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
265 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
266 | |
267 // A message with both length and offset equal zero is not forbidden | |
268 // by the spec (2.2.1.2) however it is not what is recommended. | |
269 // But test it anyway. | |
270 EXPECT_TRUE(writer.WriteSecurityBuffer(ntlm::SecurityBuffer())); | |
271 | |
272 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
273 EXPECT_EQ(OK, GetGenerateAuthTokenResult()); | |
274 } | |
275 | |
276 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithNoTargetName) { | |
277 EXPECT_EQ(OK, CreateHandler()); | |
278 | |
279 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
280 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
281 | |
282 // The spec (2.2.1.2) states that the length SHOULD be 0 and the | |
283 // offset SHOULD be where the payload would be if it was present. | |
284 // This is the expected response from a compliant server when | |
285 // no target name is sent. In reality the offset should always | |
286 // be ignored if the length is zero. Also implementations often | |
287 // just write zeros. | |
288 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
289 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 0))); | |
290 | |
291 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
292 EXPECT_EQ(OK, GetGenerateAuthTokenResult()); | |
293 } | |
294 | |
295 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithTargetName) { | |
296 EXPECT_EQ(OK, CreateHandler()); | |
297 | |
298 // One extra byte is provided for target name. | |
299 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN + 1); | |
300 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
301 | |
302 // The target name field is 1 byte long. | |
303 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
304 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 1))); | |
asanka
2017/06/23 21:29:11
Note that you are relying on NtlmBufferWriter init
zentaro
2017/07/05 17:57:41
N/A now.
| |
305 | |
306 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
307 EXPECT_EQ(OK, GetGenerateAuthTokenResult()); | |
308 } | |
309 | |
310 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromOffset) { | |
311 EXPECT_EQ(OK, CreateHandler()); | |
312 | |
313 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
314 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
315 | |
316 // Claim that the target name field is 1 byte long and outside | |
317 // the buffer. | |
318 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
319 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 1))); | |
320 | |
321 // The above malformed message could cause an implementation | |
322 // to read outside the message buffer because the offset is | |
323 // past the end of the message. Verify it gets rejected. | |
324 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
325 EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
326 } | |
327 | |
328 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromLength) { | |
329 EXPECT_EQ(OK, CreateHandler()); | |
330 | |
331 // Message has 1 extra byte of space after the header for the | |
332 // target name. | |
333 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN + 1); | |
334 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
335 // Claim that the target name field is 2 bytes long but | |
336 // there is only 1 byte of space. | |
337 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
338 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 2))); | |
339 | |
340 // The above malformed message could cause an implementation | |
341 // to read outside the message buffer because the length is | |
342 // longer than available space. Verify it gets rejected. | |
343 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
344 EXPECT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
345 } | |
346 | |
347 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3RespectsUnicode) { | |
348 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
349 MockGetHostName); | |
350 EXPECT_EQ(OK, CreateHandler()); | |
351 | |
352 // Generate the type 2 message from the server. | |
353 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
354 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
355 // No target name. Chrome doesn't use it anyway. | |
356 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
357 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 0))); | |
358 // Set the unicode flag. | |
359 EXPECT_TRUE(writer.WriteFlags(ntlm::NegotiateFlags::UNICODE)); | |
360 | |
361 std::string token; | |
362 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
363 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
364 | |
365 // Validate the type 3 message | |
366 std::string decoded; | |
367 EXPECT_TRUE(DecodeChallenge(token, &decoded)); | |
368 ntlm::NtlmBufferReader reader(decoded); | |
369 EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::AUTHENTICATE)); | |
370 | |
371 // Skip the LM and NTLM Hash fields. This test isn't testing that. | |
372 EXPECT_TRUE(reader.SkipSecurityBuffer()); | |
asanka
2017/06/23 21:29:10
SkipSecurityBufferWithValidation() ?
zentaro
2017/07/05 17:57:41
Done.
| |
373 EXPECT_TRUE(reader.SkipSecurityBuffer()); | |
374 base::string16 domain; | |
375 base::string16 username; | |
376 base::string16 hostname; | |
377 ReadString16Payload(&reader, &domain); | |
378 EXPECT_EQ(base::ASCIIToUTF16(domain_ascii_), domain); | |
379 ReadString16Payload(&reader, &username); | |
380 EXPECT_EQ(base::ASCIIToUTF16(user_ascii_), username); | |
381 ReadString16Payload(&reader, &hostname); | |
382 EXPECT_EQ(base::ASCIIToUTF16(MockGetHostName()), hostname); | |
383 | |
384 // Skip the session key which isn't used. | |
385 EXPECT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
386 | |
387 // Verify the unicode flag is set. | |
388 ntlm::NegotiateFlags flags; | |
389 EXPECT_TRUE(reader.ReadFlags(&flags)); | |
390 EXPECT_EQ(ntlm::NegotiateFlags::UNICODE, | |
391 flags & ntlm::NegotiateFlags::UNICODE); | |
392 } | |
393 | |
394 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3WithoutUnicode) { | |
395 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
396 MockGetHostName); | |
397 EXPECT_EQ(OK, CreateHandler()); | |
398 | |
399 // Generate the type 2 message from the server. | |
400 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
401 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
402 // No target name. Chrome doesn't use it anyway. | |
403 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
404 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 0))); | |
405 // Set the OEM flag. | |
406 EXPECT_TRUE(writer.WriteFlags(ntlm::NegotiateFlags::OEM)); | |
407 | |
408 std::string token; | |
409 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
410 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
411 | |
412 // Validate the type 3 message | |
413 std::string decoded; | |
414 EXPECT_TRUE(DecodeChallenge(token, &decoded)); | |
415 ntlm::NtlmBufferReader reader(decoded); | |
416 EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::AUTHENTICATE)); | |
417 | |
418 // Skip the 2 hash fields. This test isn't testing that. | |
419 EXPECT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
420 EXPECT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
421 std::string domain; | |
422 std::string username; | |
423 std::string hostname; | |
424 ReadStringPayload(&reader, &domain); | |
425 EXPECT_EQ(domain_ascii_, domain); | |
426 ReadStringPayload(&reader, &username); | |
427 EXPECT_EQ(user_ascii_, username); | |
428 ReadStringPayload(&reader, &hostname); | |
429 EXPECT_EQ(MockGetHostName(), hostname); | |
430 | |
431 // Skip the session key which isn't used. | |
432 EXPECT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
433 | |
434 // Verify the unicode flag is not set and OEM flag is. | |
435 ntlm::NegotiateFlags flags; | |
436 EXPECT_TRUE(reader.ReadFlags(&flags)); | |
437 EXPECT_EQ(ntlm::NegotiateFlags::NONE, flags & ntlm::NegotiateFlags::UNICODE); | |
438 EXPECT_EQ(ntlm::NegotiateFlags::OEM, flags & ntlm::NegotiateFlags::OEM); | |
439 } | |
440 | |
441 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3UnicodeNoSessionSecurity) { | |
442 // Verify that the client won't be downgraded if the server clears | |
443 // the session security flag. | |
444 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
445 MockGetHostName); | |
446 EXPECT_EQ(OK, CreateHandler()); | |
447 | |
448 // Generate the type 2 message from the server. | |
449 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
450 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
451 // No target name. Chrome doesn't use it anyway. | |
452 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
453 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 0))); | |
454 // Set the unicode but not the session security flag. | |
455 EXPECT_TRUE(writer.WriteFlags(ntlm::NegotiateFlags::UNICODE)); | |
456 | |
457 uint8_t server_challenge[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17}; | |
458 EXPECT_EQ(arraysize(server_challenge), ntlm::CHALLENGE_LEN); | |
459 EXPECT_TRUE(writer.WriteBytes(server_challenge, ntlm::CHALLENGE_LEN)); | |
460 EXPECT_TRUE(writer.IsEndOfBuffer()); | |
461 | |
462 std::string token; | |
463 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
464 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
465 | |
466 // Validate the type 3 message | |
467 std::string decoded; | |
468 EXPECT_TRUE(DecodeChallenge(token, &decoded)); | |
469 ntlm::NtlmBufferReader reader(decoded); | |
470 EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::AUTHENTICATE)); | |
471 | |
472 // Read the LM and NTLM Response Payloads. | |
473 uint8_t expected_lm_response[ntlm::RESPONSE_V1_LEN]; | |
474 uint8_t expected_ntlm_response[ntlm::RESPONSE_V1_LEN]; | |
475 uint8_t actual_lm_response[ntlm::RESPONSE_V1_LEN]; | |
476 uint8_t actual_ntlm_response[ntlm::RESPONSE_V1_LEN]; | |
477 | |
478 ReadBytesPayload(&reader, actual_lm_response, ntlm::RESPONSE_V1_LEN); | |
479 ReadBytesPayload(&reader, actual_ntlm_response, ntlm::RESPONSE_V1_LEN); | |
480 | |
481 // Session security also uses a client generated challenge so | |
482 // use the mock to get the same value that the implementation | |
483 // would get. | |
484 uint8_t client_challenge[ntlm::CHALLENGE_LEN]; | |
485 MockRandom(client_challenge, ntlm::CHALLENGE_LEN); | |
486 | |
487 ntlm::GenerateResponsesV1WithSS(base::ASCIIToUTF16(password_ascii_), | |
asanka
2017/06/23 21:29:11
While this makes sense for now since the test is e
zentaro
2017/07/05 17:57:41
Are you OK leaving this for this CL?
We have val
| |
488 server_challenge, client_challenge, | |
489 expected_lm_response, expected_ntlm_response); | |
490 | |
491 // Verify that the client still generated a response that uses | |
492 // session security. | |
493 EXPECT_EQ(0, memcmp(expected_lm_response, actual_lm_response, | |
494 ntlm::RESPONSE_V1_LEN)); | |
495 EXPECT_EQ(0, memcmp(expected_ntlm_response, actual_ntlm_response, | |
496 ntlm::RESPONSE_V1_LEN)); | |
497 | |
498 base::string16 domain; | |
499 base::string16 username; | |
500 base::string16 hostname; | |
501 ReadString16Payload(&reader, &domain); | |
502 EXPECT_EQ(base::ASCIIToUTF16(domain_ascii_), domain); | |
503 ReadString16Payload(&reader, &username); | |
504 EXPECT_EQ(base::ASCIIToUTF16(user_ascii_), username); | |
505 ReadString16Payload(&reader, &hostname); | |
506 EXPECT_EQ(base::ASCIIToUTF16(MockGetHostName()), hostname); | |
507 | |
508 // Skip the session key which isn't used. | |
509 EXPECT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
510 | |
511 // Verify the unicode flag is set. | |
512 ntlm::NegotiateFlags flags; | |
513 EXPECT_TRUE(reader.ReadFlags(&flags)); | |
514 EXPECT_EQ(ntlm::NegotiateFlags::UNICODE, | |
515 flags & ntlm::NegotiateFlags::UNICODE); | |
516 } | |
517 | |
518 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3UnicodeWithSessionSecurity) { | |
519 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
520 MockGetHostName); | |
521 EXPECT_EQ(OK, CreateHandler()); | |
522 | |
523 // Generate the type 2 message from the server. | |
524 ntlm::NtlmBufferWriter writer(ntlm::CHALLENGE_HEADER_LEN); | |
525 EXPECT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::CHALLENGE)); | |
526 // No target name. Chrome doesn't use it anyway. | |
527 EXPECT_TRUE(writer.WriteSecurityBuffer( | |
528 ntlm::SecurityBuffer(ntlm::CHALLENGE_HEADER_LEN, 0))); | |
529 // Set the unicode and session security flag. | |
530 EXPECT_TRUE( | |
531 writer.WriteFlags((ntlm::NegotiateFlags::UNICODE | | |
532 ntlm::NegotiateFlags::EXTENDED_SESSIONSECURITY))); | |
533 | |
534 uint8_t server_challenge[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17}; | |
535 EXPECT_EQ(arraysize(server_challenge), ntlm::CHALLENGE_LEN); | |
536 EXPECT_TRUE(writer.WriteBytes(server_challenge, ntlm::CHALLENGE_LEN)); | |
537 EXPECT_TRUE(writer.IsEndOfBuffer()); | |
538 | |
539 std::string token; | |
540 HandleAnotherChallenge(CreateType2Token(writer.GetBuffer())); | |
541 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
542 | |
543 // Validate the type 3 message | |
544 std::string decoded; | |
545 EXPECT_TRUE(DecodeChallenge(token, &decoded)); | |
546 ntlm::NtlmBufferReader reader(decoded); | |
547 EXPECT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::AUTHENTICATE)); | |
548 | |
549 // Read the LM and NTLM Response Payloads. | |
550 uint8_t expected_lm_response[ntlm::RESPONSE_V1_LEN]; | |
551 uint8_t expected_ntlm_response[ntlm::RESPONSE_V1_LEN]; | |
552 uint8_t actual_lm_response[ntlm::RESPONSE_V1_LEN]; | |
553 uint8_t actual_ntlm_response[ntlm::RESPONSE_V1_LEN]; | |
554 | |
555 ReadBytesPayload(&reader, actual_lm_response, ntlm::RESPONSE_V1_LEN); | |
556 ReadBytesPayload(&reader, actual_ntlm_response, ntlm::RESPONSE_V1_LEN); | |
557 | |
558 // Session security also uses a client generated challenge so | |
559 // use the mock to get the same value that the implementation | |
560 // would get. | |
561 uint8_t client_challenge[ntlm::CHALLENGE_LEN]; | |
562 MockRandom(client_challenge, ntlm::CHALLENGE_LEN); | |
563 | |
564 ntlm::GenerateResponsesV1WithSS(base::ASCIIToUTF16(password_ascii_), | |
565 server_challenge, client_challenge, | |
566 expected_lm_response, expected_ntlm_response); | |
567 | |
568 EXPECT_EQ(0, memcmp(expected_lm_response, actual_lm_response, | |
569 ntlm::RESPONSE_V1_LEN)); | |
570 EXPECT_EQ(0, memcmp(expected_ntlm_response, actual_ntlm_response, | |
571 ntlm::RESPONSE_V1_LEN)); | |
572 | |
573 base::string16 domain; | |
574 base::string16 username; | |
575 base::string16 hostname; | |
576 ReadString16Payload(&reader, &domain); | |
577 EXPECT_EQ(base::ASCIIToUTF16(domain_ascii_), domain); | |
578 ReadString16Payload(&reader, &username); | |
579 EXPECT_EQ(base::ASCIIToUTF16(user_ascii_), username); | |
580 ReadString16Payload(&reader, &hostname); | |
581 EXPECT_EQ(base::ASCIIToUTF16(MockGetHostName()), hostname); | |
582 | |
583 // Skip the session key which isn't used. | |
asanka
2017/06/23 21:29:10
?
zentaro
2017/07/05 17:57:42
Clarified the comment a bit. AFAIK it's only for t
| |
584 EXPECT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
585 | |
586 // Verify the unicode flag is set. | |
587 ntlm::NegotiateFlags flags; | |
588 EXPECT_TRUE(reader.ReadFlags(&flags)); | |
589 EXPECT_EQ(ntlm::NegotiateFlags::UNICODE, | |
590 flags & ntlm::NegotiateFlags::UNICODE); | |
591 } | |
592 | |
593 #endif // defined(NTLM_PORTABLE) | |
594 | |
595 } // namespace net | |
OLD | NEW |