OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
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 <string> | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "base/strings/string_util.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "net/base/test_completion_callback.h" | |
12 #include "net/dns/mock_host_resolver.h" | |
13 #include "net/http/http_auth_challenge_tokenizer.h" | |
14 #include "net/http/http_auth_handler_ntlm.h" | |
15 #include "net/http/http_request_info.h" | |
16 #include "net/http/mock_allow_http_auth_preferences.h" | |
17 #include "net/log/net_log_with_source.h" | |
18 #include "net/ntlm/ntlm.h" | |
19 #include "net/ntlm/ntlm_buffer_reader.h" | |
20 #include "net/ntlm/ntlm_buffer_writer.h" | |
21 #include "net/ntlm/ntlm_test_data.h" | |
22 #include "net/ssl/ssl_info.h" | |
23 #include "net/test/gtest_util.h" | |
24 #include "testing/gmock/include/gmock/gmock.h" | |
25 #include "testing/gtest/include/gtest/gtest.h" | |
26 #include "testing/platform_test.h" | |
27 | |
28 namespace net { | |
29 | |
30 class HttpAuthHandlerNtlmPortableTest : public PlatformTest { | |
31 public: | |
32 // Test input value defined in [MS-NLMP] Section 4.2.1. | |
33 HttpAuthHandlerNtlmPortableTest() { | |
34 http_auth_preferences_.reset(new MockAllowHttpAuthPreferences()); | |
35 factory_.reset(new HttpAuthHandlerNTLM::Factory()); | |
36 factory_->set_http_auth_preferences(http_auth_preferences_.get()); | |
37 creds_ = AuthCredentials( | |
38 ntlm::test::kNtlmDomain + base::ASCIIToUTF16("\\") + ntlm::test::kUser, | |
39 ntlm::test::kPassword); | |
40 } | |
41 | |
42 int CreateHandler() { | |
43 GURL gurl("https://foo.com"); | |
44 SSLInfo null_ssl_info; | |
45 | |
46 return factory_->CreateAuthHandlerFromString( | |
47 "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, gurl, NetLogWithSource(), | |
48 &auth_handler_); | |
49 } | |
50 | |
51 std::string CreateNtlmAuthHeader(base::StringPiece message) { | |
52 std::string output; | |
53 base::Base64Encode(message, &output); | |
54 | |
55 return "NTLM " + output; | |
56 } | |
57 | |
58 HttpAuth::AuthorizationResult HandleAnotherChallenge( | |
59 const std::string& challenge) { | |
60 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
61 return GetAuthHandler()->HandleAnotherChallenge(&tokenizer); | |
62 } | |
63 | |
64 bool DecodeChallenge(const std::string& challenge, std::string* decoded) { | |
65 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
66 return base::Base64Decode(tokenizer.base64_param(), decoded); | |
67 } | |
68 | |
69 int GenerateAuthToken(std::string* token) { | |
70 TestCompletionCallback callback; | |
71 HttpRequestInfo request_info; | |
72 return callback.GetResult(GetAuthHandler()->GenerateAuthToken( | |
73 GetCreds(), &request_info, callback.callback(), token)); | |
74 } | |
75 | |
76 void ReadBytesPayload(ntlm::NtlmBufferReader* reader, | |
77 uint8_t* buffer, | |
78 size_t len) { | |
79 // First read the security buffer. | |
80 ntlm::SecurityBuffer sec_buf; | |
81 EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf)); | |
82 EXPECT_EQ(sec_buf.length, len); | |
Ryan Sleevi
2017/07/13 17:39:54
ASSERT_EQ ?
If .length > len, this would be a cra
zentaro
2017/07/13 18:20:31
Done.
| |
83 EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, buffer)); | |
84 } | |
85 | |
86 // Reads bytes from a payload and assigns them to a string. This makes | |
87 // no assumptions about the underlying encoding. | |
88 void ReadStringPayload(ntlm::NtlmBufferReader* reader, std::string* str) { | |
89 ntlm::SecurityBuffer sec_buf; | |
90 EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf)); | |
Ryan Sleevi
2017/07/13 17:39:53
ASSERT_TRUE ?
zentaro
2017/07/13 18:20:31
Done.
| |
91 | |
92 uint8_t raw[sec_buf.length]; | |
Ryan Sleevi
2017/07/13 17:39:53
I don't believe this is legal C++, because it's a
zentaro
2017/07/13 18:20:31
Done.
| |
93 EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, raw)); | |
94 | |
95 str->assign(reinterpret_cast<const char*>(raw), sec_buf.length); | |
96 } | |
97 | |
98 // Reads bytes from a payload and assigns them to a string16. This makes | |
99 // no assumptions about the underlying encoding. This will fail if there | |
100 // are an odd number of bytes in the payload. | |
101 void ReadString16Payload(ntlm::NtlmBufferReader* reader, | |
102 base::string16* str) { | |
103 ntlm::SecurityBuffer sec_buf; | |
104 EXPECT_TRUE(reader->ReadSecurityBuffer(&sec_buf)); | |
105 EXPECT_EQ(0, sec_buf.length % 2); | |
106 | |
107 uint8_t raw[sec_buf.length]; | |
Ryan Sleevi
2017/07/13 17:39:54
Ditto re: VLAs
zentaro
2017/07/13 18:20:31
Done.
| |
108 EXPECT_TRUE(reader->ReadBytesFrom(sec_buf, raw)); | |
109 | |
110 #if IS_BIG_ENDIAN | |
Ryan Sleevi
2017/07/13 17:39:53
The code elsewhere (e.g. http_auth_handler_ntlm_po
zentaro
2017/07/13 18:20:31
Done.
| |
111 for (size_t i = 0; i < sec_buf.length; i += 2) { | |
112 std::swap(raw[i], raw[i + 1]); | |
113 } | |
114 #endif | |
115 | |
116 str->assign(reinterpret_cast<const base::char16*>(raw), sec_buf.length / 2); | |
117 } | |
118 | |
119 int GetGenerateAuthTokenResult() { | |
120 std::string token; | |
121 return GenerateAuthToken(&token); | |
122 } | |
123 | |
124 AuthCredentials* GetCreds() { return &creds_; } | |
125 | |
126 HttpAuthHandlerNTLM* GetAuthHandler() { | |
127 return static_cast<HttpAuthHandlerNTLM*>(auth_handler_.get()); | |
128 } | |
129 | |
130 static void MockRandom(uint8_t* output, size_t n) { | |
131 // This is set to 0xaa because the client challenge for testing in | |
132 // [MS-NLMP] Section 4.2.1 is 8 bytes of 0xaa. | |
133 memset(output, 0xaa, n); | |
134 } | |
135 | |
136 static std::string MockGetHostName() { return ntlm::test::kHostnameAscii; } | |
137 | |
138 private: | |
139 AuthCredentials creds_; | |
140 std::unique_ptr<HttpAuthHandler> auth_handler_; | |
141 std::unique_ptr<MockAllowHttpAuthPreferences> http_auth_preferences_; | |
142 std::unique_ptr<HttpAuthHandlerNTLM::Factory> factory_; | |
143 }; | |
144 | |
145 TEST_F(HttpAuthHandlerNtlmPortableTest, SimpleConstruction) { | |
146 ASSERT_EQ(OK, CreateHandler()); | |
147 ASSERT_TRUE(GetAuthHandler() != nullptr); | |
148 } | |
149 | |
150 TEST_F(HttpAuthHandlerNtlmPortableTest, DoNotAllowDefaultCreds) { | |
151 ASSERT_EQ(OK, CreateHandler()); | |
152 ASSERT_FALSE(GetAuthHandler()->AllowsDefaultCredentials()); | |
153 } | |
154 | |
155 TEST_F(HttpAuthHandlerNtlmPortableTest, AllowsExplicitCredentials) { | |
156 ASSERT_EQ(OK, CreateHandler()); | |
157 ASSERT_TRUE(GetAuthHandler()->AllowsExplicitCredentials()); | |
158 } | |
159 | |
160 TEST_F(HttpAuthHandlerNtlmPortableTest, VerifyType1Message) { | |
161 ASSERT_EQ(OK, CreateHandler()); | |
162 | |
163 std::string token; | |
164 ASSERT_EQ(OK, GenerateAuthToken(&token)); | |
165 // The type 1 message generated is always the same. The only variable | |
166 // part of the message is the flags and this implementation always offers | |
167 // the same set of flags. | |
168 ASSERT_EQ("NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA=", token); | |
169 } | |
170 | |
171 TEST_F(HttpAuthHandlerNtlmPortableTest, EmptyTokenFails) { | |
172 ASSERT_EQ(OK, CreateHandler()); | |
173 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
174 | |
175 // The encoded token for a type 2 message can't be empty. | |
176 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
177 HandleAnotherChallenge("NTLM")); | |
178 } | |
179 | |
180 TEST_F(HttpAuthHandlerNtlmPortableTest, InvalidBase64Encoding) { | |
181 ASSERT_EQ(OK, CreateHandler()); | |
182 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
183 | |
184 // Token isn't valid base64. | |
185 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
186 HandleAnotherChallenge("NTLM !!!!!!!!!!!!!")); | |
187 ASSERT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
188 } | |
189 | |
190 TEST_F(HttpAuthHandlerNtlmPortableTest, CantChangeSchemeMidway) { | |
191 ASSERT_EQ(OK, CreateHandler()); | |
192 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
193 | |
194 // Can't switch to a different auth scheme in the middle of the process. | |
195 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID, | |
196 HandleAnotherChallenge("Negotiate SSdtIG5vdCBhIHJlYWwgdG9rZW4h")); | |
197 } | |
198 | |
199 TEST_F(HttpAuthHandlerNtlmPortableTest, MinimalStructurallyValidType2) { | |
200 ASSERT_EQ(OK, CreateHandler()); | |
201 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
202 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
203 HandleAnotherChallenge(CreateNtlmAuthHeader(base::StringPiece( | |
204 reinterpret_cast<const char*>(ntlm::test::kMinChallengeMessage), | |
205 ntlm::kChallengeHeaderLen)))); | |
206 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
207 } | |
208 | |
209 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageTooShort) { | |
210 ASSERT_EQ(OK, CreateHandler()); | |
211 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
212 | |
213 char raw[31]; | |
214 memcpy(raw, ntlm::test::kMinChallengeMessage, 31); | |
215 | |
216 // Fail because the minimum size valid message is 32 bytes. | |
217 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
218 HandleAnotherChallenge( | |
219 CreateNtlmAuthHeader(base::StringPiece(raw, sizeof(raw))))); | |
220 ASSERT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
221 } | |
222 | |
223 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWrongSignature) { | |
224 ASSERT_EQ(OK, CreateHandler()); | |
225 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
226 | |
227 char raw[32]; | |
228 memcpy(raw, ntlm::test::kMinChallengeMessage, 32); | |
229 // Modify the default valid message to overwrite the last byte of the | |
230 // signature. | |
231 raw[7] = 0xff; | |
232 | |
233 // Fail because the first 8 bytes don't match "NTLMSSP\0" | |
234 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
235 HandleAnotherChallenge( | |
236 CreateNtlmAuthHeader(base::StringPiece(raw, sizeof(raw))))); | |
237 ASSERT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
238 } | |
239 | |
240 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2WrongMessageType) { | |
241 ASSERT_EQ(OK, CreateHandler()); | |
242 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
243 | |
244 char raw[32]; | |
245 memcpy(raw, ntlm::test::kMinChallengeMessage, 32); | |
246 // Modify the message type so it is not 0x00000002 | |
247 raw[8] = 0x03; | |
248 | |
249 // Fail because the message type should be MessageType::kChallenge | |
250 // (0x00000002) | |
251 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
252 HandleAnotherChallenge(CreateNtlmAuthHeader(raw))); | |
253 ASSERT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
254 } | |
255 | |
256 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithNoTargetName) { | |
257 ASSERT_EQ(OK, CreateHandler()); | |
258 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
259 | |
260 // The spec (2.2.1.2) states that the length SHOULD be 0 and the offset | |
261 // SHOULD be where the payload would be if it was present. This is the | |
262 // expected response from a compliant server when no target name is sent. | |
263 // In reality the offset should always be ignored if the length is zero. | |
264 // Also implementations often just write zeros. | |
265 char raw[32]; | |
266 memcpy(raw, ntlm::test::kMinChallengeMessage, 32); | |
267 // Modify the default valid message to overwrite the offset to zero. | |
268 raw[16] = 0x00; | |
269 | |
270 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
271 HandleAnotherChallenge( | |
272 CreateNtlmAuthHeader(base::StringPiece(raw, sizeof(raw))))); | |
273 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
274 } | |
275 | |
276 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithTargetName) { | |
277 ASSERT_EQ(OK, CreateHandler()); | |
278 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
279 | |
280 // One extra byte is provided for target name. | |
281 char raw[33]; | |
282 memcpy(raw, ntlm::test::kMinChallengeMessage, 32); | |
283 // Modify the default valid message to indicate 1 byte is present in the | |
284 // target name payload. | |
285 raw[12] = 0x01; | |
286 raw[14] = 0x01; | |
287 // Put something in the target name. | |
288 raw[32] = 'Z'; | |
289 | |
290 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
291 HandleAnotherChallenge( | |
292 CreateNtlmAuthHeader(base::StringPiece(raw, sizeof(raw))))); | |
293 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
294 } | |
295 | |
296 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromOffset) { | |
297 ASSERT_EQ(OK, CreateHandler()); | |
298 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
299 | |
300 char raw[32]; | |
301 memcpy(raw, ntlm::test::kMinChallengeMessage, 32); | |
302 // Modify the default valid message to claim that the target name field is 1 | |
303 // byte long overrunning the end of the message message. | |
304 raw[12] = 0x01; | |
305 raw[14] = 0x01; | |
306 | |
307 // The above malformed message could cause an implementation to read outside | |
308 // the message buffer because the offset is past the end of the message. | |
309 // Verify it gets rejected. | |
310 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
311 HandleAnotherChallenge( | |
312 CreateNtlmAuthHeader(base::StringPiece(raw, sizeof(raw))))); | |
313 ASSERT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
314 } | |
315 | |
316 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromLength) { | |
317 ASSERT_EQ(OK, CreateHandler()); | |
318 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
319 | |
320 // Message has 1 extra byte of space after the header for the target name. | |
321 // One extra byte is provided for target name. | |
322 char raw[33]; | |
323 memcpy(raw, ntlm::test::kMinChallengeMessage, 32); | |
324 // Modify the default valid message to indicate 2 bytes are present in the | |
325 // target name payload (however there is only space for 1). | |
326 raw[12] = 0x02; | |
327 raw[14] = 0x02; | |
328 // Put something in the target name. | |
329 raw[32] = 'Z'; | |
330 | |
331 // The above malformed message could cause an implementation to read outside | |
332 // the message buffer because the length is longer than available space. | |
333 // Verify it gets rejected. | |
334 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
335 HandleAnotherChallenge( | |
336 CreateNtlmAuthHeader(base::StringPiece(raw, sizeof(raw))))); | |
337 ASSERT_EQ(ERR_UNEXPECTED, GetGenerateAuthTokenResult()); | |
338 } | |
339 | |
340 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3RespectsUnicode) { | |
341 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
342 MockGetHostName); | |
343 ASSERT_EQ(OK, CreateHandler()); | |
344 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
345 | |
346 // Generate the type 2 message from the server. | |
347 ntlm::NtlmBufferWriter writer(ntlm::kChallengeHeaderLen); | |
348 ASSERT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::kChallenge)); | |
349 // No target name. It is never used. | |
350 ASSERT_TRUE(writer.WriteSecurityBuffer( | |
351 ntlm::SecurityBuffer(ntlm::kChallengeHeaderLen, 0))); | |
352 // Set the unicode flag. | |
353 ASSERT_TRUE(writer.WriteFlags(ntlm::NegotiateFlags::kUnicode)); | |
354 | |
355 std::string token; | |
356 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
357 HandleAnotherChallenge(CreateNtlmAuthHeader(writer.GetBuffer()))); | |
358 ASSERT_EQ(OK, GenerateAuthToken(&token)); | |
359 | |
360 // Validate the type 3 message | |
361 std::string decoded; | |
362 ASSERT_TRUE(DecodeChallenge(token, &decoded)); | |
363 ntlm::NtlmBufferReader reader(decoded); | |
364 ASSERT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::kAuthenticate)); | |
365 | |
366 // Skip the LM and NTLM Hash fields. This test isn't testing that. | |
367 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
368 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
369 base::string16 domain; | |
370 base::string16 username; | |
371 base::string16 hostname; | |
372 ReadString16Payload(&reader, &domain); | |
373 ASSERT_EQ(ntlm::test::kNtlmDomain, domain); | |
374 ReadString16Payload(&reader, &username); | |
375 ASSERT_EQ(ntlm::test::kUser, username); | |
376 ReadString16Payload(&reader, &hostname); | |
377 ASSERT_EQ(ntlm::test::kHostname, hostname); | |
378 | |
379 // The session key is not used for the NTLM scheme in HTTP. Since | |
380 // NTLMSSP_NEGOTIATE_KEY_EXCH was not sent this is empty. | |
381 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
382 | |
383 // Verify the unicode flag is set. | |
384 ntlm::NegotiateFlags flags; | |
385 ASSERT_TRUE(reader.ReadFlags(&flags)); | |
386 ASSERT_EQ(ntlm::NegotiateFlags::kUnicode, | |
387 flags & ntlm::NegotiateFlags::kUnicode); | |
388 } | |
389 | |
390 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3WithoutUnicode) { | |
391 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
392 MockGetHostName); | |
393 ASSERT_EQ(OK, CreateHandler()); | |
394 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
395 | |
396 // Generate the type 2 message from the server. | |
397 ntlm::NtlmBufferWriter writer(ntlm::kChallengeHeaderLen); | |
398 ASSERT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::kChallenge)); | |
399 // No target name. It is never used. | |
400 ASSERT_TRUE(writer.WriteSecurityBuffer( | |
401 ntlm::SecurityBuffer(ntlm::kChallengeHeaderLen, 0))); | |
402 // Set the OEM flag. | |
403 ASSERT_TRUE(writer.WriteFlags(ntlm::NegotiateFlags::kOem)); | |
404 | |
405 std::string token; | |
406 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
407 HandleAnotherChallenge(CreateNtlmAuthHeader(writer.GetBuffer()))); | |
408 ASSERT_EQ(OK, GenerateAuthToken(&token)); | |
409 | |
410 // Validate the type 3 message | |
411 std::string decoded; | |
412 ASSERT_TRUE(DecodeChallenge(token, &decoded)); | |
413 ntlm::NtlmBufferReader reader(decoded); | |
414 ASSERT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::kAuthenticate)); | |
415 | |
416 // Skip the 2 hash fields. This test isn't testing that. | |
417 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
418 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
419 std::string domain; | |
420 std::string username; | |
421 std::string hostname; | |
422 ReadStringPayload(&reader, &domain); | |
423 ASSERT_EQ(ntlm::test::kNtlmDomainAscii, domain); | |
424 ReadStringPayload(&reader, &username); | |
425 ASSERT_EQ(ntlm::test::kUserAscii, username); | |
426 ReadStringPayload(&reader, &hostname); | |
427 ASSERT_EQ(ntlm::test::kHostnameAscii, hostname); | |
428 | |
429 // The session key is not used for the NTLM scheme in HTTP. Since | |
430 // NTLMSSP_NEGOTIATE_KEY_EXCH was not sent this is empty. | |
431 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
432 | |
433 // Verify the unicode flag is not set and OEM flag is. | |
434 ntlm::NegotiateFlags flags; | |
435 ASSERT_TRUE(reader.ReadFlags(&flags)); | |
436 ASSERT_EQ(ntlm::NegotiateFlags::kNone, | |
437 flags & ntlm::NegotiateFlags::kUnicode); | |
438 ASSERT_EQ(ntlm::NegotiateFlags::kOem, flags & ntlm::NegotiateFlags::kOem); | |
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 ASSERT_EQ(OK, CreateHandler()); | |
447 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
448 | |
449 // Generate the type 2 message from the server. | |
450 ntlm::NtlmBufferWriter writer(ntlm::kChallengeHeaderLen); | |
451 ASSERT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::kChallenge)); | |
452 // No target name. It is never used. | |
453 ASSERT_TRUE(writer.WriteSecurityBuffer( | |
454 ntlm::SecurityBuffer(ntlm::kChallengeHeaderLen, 0))); | |
455 // Set the unicode but not the session security flag. | |
456 ASSERT_TRUE(writer.WriteFlags(ntlm::NegotiateFlags::kUnicode)); | |
457 | |
458 ASSERT_TRUE( | |
459 writer.WriteBytes(ntlm::test::kServerChallenge, ntlm::kChallengeLen)); | |
460 ASSERT_TRUE(writer.IsEndOfBuffer()); | |
461 | |
462 std::string token; | |
463 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
464 HandleAnotherChallenge(CreateNtlmAuthHeader(writer.GetBuffer()))); | |
465 ASSERT_EQ(OK, GenerateAuthToken(&token)); | |
466 | |
467 // Validate the type 3 message | |
468 std::string decoded; | |
469 ASSERT_TRUE(DecodeChallenge(token, &decoded)); | |
470 ntlm::NtlmBufferReader reader(decoded); | |
471 ASSERT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::kAuthenticate)); | |
472 | |
473 // Read the LM and NTLM Response Payloads. | |
474 uint8_t actual_lm_response[ntlm::kResponseLenV1]; | |
475 uint8_t actual_ntlm_response[ntlm::kResponseLenV1]; | |
476 ReadBytesPayload(&reader, actual_lm_response, ntlm::kResponseLenV1); | |
477 ReadBytesPayload(&reader, actual_ntlm_response, ntlm::kResponseLenV1); | |
478 | |
479 // Verify that the client still generated a response that uses | |
480 // session security. | |
481 ASSERT_EQ(0, memcmp(ntlm::test::kExpectedLmResponseWithV1SS, | |
482 actual_lm_response, ntlm::kResponseLenV1)); | |
483 ASSERT_EQ(0, memcmp(ntlm::test::kExpectedNtlmResponseWithV1SS, | |
484 actual_ntlm_response, ntlm::kResponseLenV1)); | |
485 | |
486 base::string16 domain; | |
487 base::string16 username; | |
488 base::string16 hostname; | |
489 ReadString16Payload(&reader, &domain); | |
490 ASSERT_EQ(ntlm::test::kNtlmDomain, domain); | |
491 ReadString16Payload(&reader, &username); | |
492 ASSERT_EQ(ntlm::test::kUser, username); | |
493 ReadString16Payload(&reader, &hostname); | |
494 ASSERT_EQ(ntlm::test::kHostname, hostname); | |
495 | |
496 // The session key is not used for the NTLM scheme in HTTP. Since | |
497 // NTLMSSP_NEGOTIATE_KEY_EXCH was not sent this is empty. | |
498 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
499 | |
500 // Verify the unicode flag is set. | |
501 ntlm::NegotiateFlags flags; | |
502 ASSERT_TRUE(reader.ReadFlags(&flags)); | |
503 ASSERT_EQ(ntlm::NegotiateFlags::kUnicode, | |
504 flags & ntlm::NegotiateFlags::kUnicode); | |
505 } | |
506 | |
507 TEST_F(HttpAuthHandlerNtlmPortableTest, Type3UnicodeWithSessionSecurity) { | |
508 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
509 MockGetHostName); | |
510 ASSERT_EQ(OK, CreateHandler()); | |
511 ASSERT_EQ(OK, GetGenerateAuthTokenResult()); | |
512 | |
513 // Generate the type 2 message from the server. | |
514 ntlm::NtlmBufferWriter writer(ntlm::kChallengeHeaderLen); | |
515 ASSERT_TRUE(writer.WriteMessageHeader(ntlm::MessageType::kChallenge)); | |
516 // No target name. It is never used. | |
517 ASSERT_TRUE(writer.WriteSecurityBuffer( | |
518 ntlm::SecurityBuffer(ntlm::kChallengeHeaderLen, 0))); | |
519 // Set the unicode and session security flag. | |
520 ASSERT_TRUE( | |
521 writer.WriteFlags((ntlm::NegotiateFlags::kUnicode | | |
522 ntlm::NegotiateFlags::kExtendedSessionSecurity))); | |
523 | |
524 ASSERT_TRUE( | |
525 writer.WriteBytes(ntlm::test::kServerChallenge, ntlm::kChallengeLen)); | |
526 ASSERT_TRUE(writer.IsEndOfBuffer()); | |
527 | |
528 std::string token; | |
529 ASSERT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
530 HandleAnotherChallenge(CreateNtlmAuthHeader(writer.GetBuffer()))); | |
531 ASSERT_EQ(OK, GenerateAuthToken(&token)); | |
532 | |
533 // Validate the type 3 message | |
534 std::string decoded; | |
535 ASSERT_TRUE(DecodeChallenge(token, &decoded)); | |
536 ntlm::NtlmBufferReader reader(decoded); | |
537 ASSERT_TRUE(reader.MatchMessageHeader(ntlm::MessageType::kAuthenticate)); | |
538 | |
539 // Read the LM and NTLM Response Payloads. | |
540 uint8_t actual_lm_response[ntlm::kResponseLenV1]; | |
541 uint8_t actual_ntlm_response[ntlm::kResponseLenV1]; | |
542 ReadBytesPayload(&reader, actual_lm_response, ntlm::kResponseLenV1); | |
543 ReadBytesPayload(&reader, actual_ntlm_response, ntlm::kResponseLenV1); | |
544 | |
545 ASSERT_EQ(0, memcmp(ntlm::test::kExpectedLmResponseWithV1SS, | |
546 actual_lm_response, ntlm::kResponseLenV1)); | |
547 ASSERT_EQ(0, memcmp(ntlm::test::kExpectedNtlmResponseWithV1SS, | |
548 actual_ntlm_response, ntlm::kResponseLenV1)); | |
549 | |
550 base::string16 domain; | |
551 base::string16 username; | |
552 base::string16 hostname; | |
553 ReadString16Payload(&reader, &domain); | |
554 ASSERT_EQ(ntlm::test::kNtlmDomain, domain); | |
555 ReadString16Payload(&reader, &username); | |
556 ASSERT_EQ(ntlm::test::kUser, username); | |
557 ReadString16Payload(&reader, &hostname); | |
558 ASSERT_EQ(ntlm::test::kHostname, hostname); | |
559 | |
560 // The session key is not used for the NTLM scheme in HTTP. Since | |
561 // NTLMSSP_NEGOTIATE_KEY_EXCH was not sent this is empty. | |
562 ASSERT_TRUE(reader.SkipSecurityBufferWithValidation()); | |
563 | |
564 // Verify the unicode flag is set. | |
565 ntlm::NegotiateFlags flags; | |
566 ASSERT_TRUE(reader.ReadFlags(&flags)); | |
567 ASSERT_EQ(ntlm::NegotiateFlags::kUnicode, | |
568 flags & ntlm::NegotiateFlags::kUnicode); | |
569 } | |
570 | |
571 } // namespace net | |
OLD | NEW |