OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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 "net/http/http_auth_handler_ntlm.h" | |
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/net_errors.h" | |
14 #include "net/base/test_completion_callback.h" | |
15 #include "net/dns/mock_host_resolver.h" | |
16 #include "net/http/http_auth_challenge_tokenizer.h" | |
17 #include "net/http/http_request_info.h" | |
18 #include "net/http/mock_allow_http_auth_preferences.h" | |
19 #include "net/log/net_log_with_source.h" | |
20 #include "net/ssl/ssl_info.h" | |
21 #include "net/test/gtest_util.h" | |
22 #include "testing/gmock/include/gmock/gmock.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 #include "testing/platform_test.h" | |
25 | |
26 using net::test::IsError; | |
27 using net::test::IsOk; | |
28 | |
29 namespace net { | |
30 | |
31 #if defined(NTLM_PORTABLE) | |
32 | |
33 const char NTLM_SIGNATURE[] = "NTLMSSP"; | |
34 | |
35 class HttpAuthHandlerNtlmPortableTest : public PlatformTest { | |
36 public: | |
37 void SetUp() override { | |
38 http_auth_preferences_.reset(new MockAllowHttpAuthPreferences()); | |
39 factory_.reset(new HttpAuthHandlerNTLM::Factory()); | |
40 factory_->set_http_auth_preferences(http_auth_preferences_.get()); | |
41 creds_ = AuthCredentials(base::ASCIIToUTF16("someuser"), | |
42 base::ASCIIToUTF16("badpassword")); | |
Ryan Sleevi
2017/05/09 20:49:27
Any reason for the SetUp vs CTOR?
(See https://gi
zentaro
2017/05/15 20:48:38
Nope. I just copied from the Negotiate one as a st
| |
43 } | |
44 | |
45 int CreateHandler() { | |
46 GURL gurl("foo.com"); | |
Ryan Sleevi
2017/05/09 20:49:27
Since you're creating a GURL, let's use a proper U
zentaro
2017/05/15 20:48:37
Done.
| |
47 SSLInfo null_ssl_info; | |
48 | |
49 return factory_->CreateAuthHandlerFromString( | |
50 "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, gurl, NetLogWithSource(), | |
51 &auth_handler_); | |
52 } | |
53 | |
54 std::string CreateType2Token(const char* message, size_t message_len) { | |
Ryan Sleevi
2017/05/09 20:49:27
Consider using base::StringPiece ?
zentaro
2017/05/15 20:48:38
Done.
| |
55 std::string output; | |
56 std::string input(message, message_len); | |
57 base::Base64Encode(input, &output); | |
58 | |
59 return "NTLM " + output; | |
60 } | |
61 | |
62 void HandleAnotherChallenge(std::string challenge) { | |
Ryan Sleevi
2017/05/09 20:49:27
Pass this by const-ref? Or did you mean to explici
zentaro
2017/05/15 20:48:37
Done.
| |
63 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
64 EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
65 GetAuthHandler()->HandleAnotherChallenge(&tokenizer)); | |
66 } | |
67 | |
68 int GenerateAuthToken(std::string* token) { | |
69 TestCompletionCallback callback; | |
70 HttpRequestInfo request_info; | |
71 return callback.GetResult(GetAuthHandler()->GenerateAuthToken( | |
72 GetCreds(), &request_info, callback.callback(), token)); | |
Ryan Sleevi
2017/05/09 20:49:27
There's no way to wait for the callback's result s
zentaro
2017/05/15 20:48:37
Looks like TestCompletionCallback::GetResult check
| |
73 } | |
74 | |
75 int GenerateAuthToken() { | |
Ryan Sleevi
2017/05/09 20:49:27
Why this overload? This is a fairly surprising nam
zentaro
2017/05/15 20:48:37
As above I think this runs synchronously. The over
| |
76 std::string token; | |
77 return GenerateAuthToken(&token); | |
78 } | |
79 | |
80 AuthCredentials* GetCreds() { return &creds_; } | |
81 | |
82 HttpAuthHandlerNTLM* GetAuthHandler() { | |
83 return static_cast<HttpAuthHandlerNTLM*>(auth_handler_.get()); | |
84 } | |
85 | |
86 static void MockRandom(uint8_t* output, size_t n) { | |
87 static const uint8_t bytes[] = {0x55, 0x29, 0x66, 0x26, | |
88 0x6b, 0x9c, 0x73, 0x54}; | |
89 static size_t current_byte = 0; | |
90 for (size_t i = 0; i < n; ++i) { | |
91 output[i] = bytes[current_byte++]; | |
92 current_byte %= arraysize(bytes); | |
93 } | |
94 } | |
95 | |
96 static std::string MockGetHostName() { return "MYHOSTNAME"; } | |
97 | |
98 private: | |
99 AuthCredentials creds_; | |
100 std::unique_ptr<HttpAuthHandler> auth_handler_; | |
101 std::unique_ptr<MockAllowHttpAuthPreferences> http_auth_preferences_; | |
102 std::unique_ptr<HttpAuthHandlerNTLM::Factory> factory_; | |
103 }; | |
104 | |
105 TEST_F(HttpAuthHandlerNtlmPortableTest, SimpleConstruction) { | |
106 EXPECT_EQ(OK, CreateHandler()); | |
107 ASSERT_TRUE(GetAuthHandler() != nullptr); | |
108 } | |
109 | |
110 TEST_F(HttpAuthHandlerNtlmPortableTest, DoNotAllowDefaultCreds) { | |
111 EXPECT_EQ(OK, CreateHandler()); | |
112 EXPECT_FALSE(GetAuthHandler()->AllowsDefaultCredentials()); | |
113 } | |
114 | |
115 TEST_F(HttpAuthHandlerNtlmPortableTest, AllowsExplicitCredentials) { | |
116 EXPECT_EQ(OK, CreateHandler()); | |
117 EXPECT_TRUE(GetAuthHandler()->AllowsExplicitCredentials()); | |
118 } | |
119 | |
120 TEST_F(HttpAuthHandlerNtlmPortableTest, VerifyType1Message) { | |
121 EXPECT_EQ(OK, CreateHandler()); | |
122 | |
123 std::string token; | |
124 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
125 // The type 1 message generated is always the same. The only variable | |
126 // part of the message is the flags and Chrome always offers the same | |
127 // set of flags. | |
128 EXPECT_EQ("NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA=", token); | |
129 } | |
130 | |
131 TEST_F(HttpAuthHandlerNtlmPortableTest, ValidType2Input) { | |
132 HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockRandom, | |
133 MockGetHostName); | |
134 EXPECT_EQ(OK, CreateHandler()); | |
135 | |
136 // This is a real type 2 message sent by a Windows Server in response to | |
137 // Chrome's type 1 message. | |
Ryan Sleevi
2017/05/09 20:49:27
How was this computed? What are the values? How ca
zentaro
2017/05/15 20:48:38
It was just copied from a real transaction. I was
| |
138 std::string challenge_str = | |
139 "NTLM " | |
140 "TlRMTVNTUAACAAAADAAMADgAAAAFgokCXziKeNIPIDYAAAAAAAAAAIYAhgBEAAAABgOAJQAA" | |
141 "AA9aAEUATgBEAE8ATQACAAwAWgBFAE4ARABPAE0AAQAMAFoARQBOAEQAQwAxAAQAFAB6AGUA" | |
142 "bgBkAG8AbQAuAGwAbwBjAAMAIgBaAGUAbgBEAEMAMQAuAHoAZQBuAGQAbwBtAC4AbABvAGMA" | |
143 "BQAUAHoAZQBuAGQAbwBtAC4AbABvAGMABwAIAN6N+IxGwNIBAAAAAA=="; | |
144 std::string token; | |
145 HandleAnotherChallenge(challenge_str); | |
146 EXPECT_EQ(OK, GenerateAuthToken(&token)); | |
147 | |
148 // Type 3 message based on mocked RNG data. | |
149 EXPECT_EQ( | |
150 "NTLM " | |
151 "TlRMTVNTUAADAAAAGAAYAGQAAAAYABgAfAAAAAAAAABAAAAAEAAQAEAAAAAUABQAUAAAAAAA" | |
152 "AAAAAAAABYIIAHMAbwBtAGUAdQBzAGUAcgBNAFkASABPAFMAVABOAEEATQBFAFUpZiZrnHNU" | |
153 "AAAAAAAAAAAAAAAAAAAAAOYv6VCd3XSbN+S4BO5kmGWvVzlFBGnJOg==", | |
154 token); | |
155 } | |
156 | |
157 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageTooShort) { | |
158 EXPECT_EQ(OK, CreateHandler()); | |
159 | |
160 // Fail because the minimum size valid message is 32 bytes. | |
161 char raw[31]; | |
162 memset(raw, 0, sizeof(raw)); | |
163 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
164 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
165 } | |
166 | |
167 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageNoSig) { | |
168 EXPECT_EQ(OK, CreateHandler()); | |
169 | |
170 // Fail because the first bytes don't match "NTLMSSP\0" | |
171 char raw[32]; | |
172 memset(raw, 0, sizeof(raw)); | |
173 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
174 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
175 } | |
176 | |
177 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2WrongMessageType) { | |
178 EXPECT_EQ(OK, CreateHandler()); | |
179 | |
180 // Fail because the message type (starting byte 8) should be 0x00000002. | |
181 char raw[32]; | |
182 memset(raw, 0, sizeof(raw)); | |
183 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
184 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
185 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
186 } | |
187 | |
188 TEST_F(HttpAuthHandlerNtlmPortableTest, MinimalStructurallyValidType2) { | |
189 EXPECT_EQ(OK, CreateHandler()); | |
190 | |
191 char raw[32]; | |
192 memset(raw, 0, sizeof(raw)); | |
193 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
194 // Mark as a type 2 message. | |
195 raw[8] = 2; | |
196 | |
197 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
198 EXPECT_EQ(OK, GenerateAuthToken()); | |
199 } | |
200 | |
201 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithNoTargetName) { | |
202 EXPECT_EQ(OK, CreateHandler()); | |
203 | |
204 char raw[32]; | |
205 memset(raw, 0, sizeof(raw)); | |
206 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
207 // Mark as a type 2 message. | |
208 raw[8] = 2; | |
209 // Point the offset to the end of the buffer. | |
210 raw[16] = 32; | |
211 | |
212 // Although the MinimalStructurallyValidType2 message with both | |
213 // length and offset equal zero is not forbidden by the spec, the | |
214 // spec (2.2.1.2) states that the length SHOULD be 0 and the | |
215 // offset SHOULD be where the payload would be if it was present. | |
216 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
217 EXPECT_EQ(OK, GenerateAuthToken()); | |
218 } | |
219 | |
220 TEST_F(HttpAuthHandlerNtlmPortableTest, Type2MessageWithTargetName) { | |
221 EXPECT_EQ(OK, CreateHandler()); | |
222 | |
223 // One extra byte is provided for target name. | |
224 char raw[33]; | |
225 memset(raw, 0, sizeof(raw)); | |
226 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
227 // Mark as a type 2 message. | |
228 raw[8] = 2; | |
229 // The target name field is 1 byte long. | |
230 raw[12] = 1; | |
231 raw[14] = 1; | |
232 // Point the offset to the extra 1 byte space. | |
233 raw[16] = 32; | |
234 | |
235 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
236 EXPECT_EQ(OK, GenerateAuthToken()); | |
237 } | |
238 | |
239 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromOffset) { | |
240 EXPECT_EQ(OK, CreateHandler()); | |
241 | |
242 char raw[32]; | |
243 memset(raw, 0, sizeof(raw)); | |
244 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
245 // Mark as a type 2 message. | |
246 raw[8] = 2; | |
247 // Claim that the target name field is 1 byte long. | |
248 raw[12] = 1; | |
249 raw[14] = 1; | |
250 // Point the offset outside the message buffer. | |
251 raw[16] = 32; | |
252 | |
253 // The above malformed message could cause an implementation | |
254 // to read outside the message buffer because the offset is | |
255 // past the end of the message. Verify it gets rejected. | |
256 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
257 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
258 } | |
259 | |
260 TEST_F(HttpAuthHandlerNtlmPortableTest, NoTargetNameOverflowFromLength) { | |
261 EXPECT_EQ(OK, CreateHandler()); | |
262 | |
263 // Message has 1 extra byte of space after the header for the | |
264 // target name. | |
265 char raw[33]; | |
266 memset(raw, 0, sizeof(raw)); | |
267 memcpy(raw, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); | |
268 // Mark as a type 2 message. | |
269 raw[8] = 2; | |
270 // Claim that the target name field is 2 bytes long. | |
271 raw[12] = 2; | |
272 raw[14] = 2; | |
273 // Point the offset to the extra 1 byte space. | |
274 raw[16] = 32; | |
275 | |
276 // The above malformed message could cause an implementation | |
277 // to read outside the message buffer because the length is | |
278 // longer than available space. Verify it gets rejected. | |
279 HandleAnotherChallenge(CreateType2Token(raw, sizeof(raw))); | |
280 EXPECT_EQ(ERR_UNEXPECTED, GenerateAuthToken()); | |
281 } | |
282 | |
283 #endif // defined(NTLM_PORTABLE) | |
284 | |
285 } // namespace net | |
OLD | NEW |