1 // Copyright 2011 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 "net/http/http_auth_handler_factory.h"
6
7 #include <memory>
8
9 #include "base/functional/bind.h"
10 #include "base/functional/callback.h"
11 #include "build/build_config.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/network_isolation_key.h"
14 #include "net/dns/host_resolver.h"
15 #include "net/dns/mock_host_resolver.h"
16 #include "net/http/http_auth_handler.h"
17 #include "net/http/http_auth_scheme.h"
18 #include "net/http/mock_allow_http_auth_preferences.h"
19 #include "net/http/url_security_manager.h"
20 #include "net/log/net_log_values.h"
21 #include "net/log/net_log_with_source.h"
22 #include "net/log/test_net_log.h"
23 #include "net/net_buildflags.h"
24 #include "net/ssl/ssl_info.h"
25 #include "net/test/gtest_util.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "url/gurl.h"
29 #include "url/scheme_host_port.h"
30
31 using net::test::IsError;
32 using net::test::IsOk;
33
34 namespace net {
35
36 namespace {
37
38 class MockHttpAuthHandlerFactory : public HttpAuthHandlerFactory {
39 public:
MockHttpAuthHandlerFactory(int return_code)40 explicit MockHttpAuthHandlerFactory(int return_code) :
41 return_code_(return_code) {}
42 ~MockHttpAuthHandlerFactory() override = default;
43
CreateAuthHandler(HttpAuthChallengeTokenizer * challenge,HttpAuth::Target target,const SSLInfo & ssl_info,const NetworkAnonymizationKey & network_anonymization_key,const url::SchemeHostPort & scheme_host_port,CreateReason reason,int nonce_count,const NetLogWithSource & net_log,HostResolver * host_resolver,std::unique_ptr<HttpAuthHandler> * handler)44 int CreateAuthHandler(
45 HttpAuthChallengeTokenizer* challenge,
46 HttpAuth::Target target,
47 const SSLInfo& ssl_info,
48 const NetworkAnonymizationKey& network_anonymization_key,
49 const url::SchemeHostPort& scheme_host_port,
50 CreateReason reason,
51 int nonce_count,
52 const NetLogWithSource& net_log,
53 HostResolver* host_resolver,
54 std::unique_ptr<HttpAuthHandler>* handler) override {
55 handler->reset();
56 return return_code_;
57 }
58
59 private:
60 int return_code_;
61 };
62
63 } // namespace
64
TEST(HttpAuthHandlerFactoryTest,RegistryFactory)65 TEST(HttpAuthHandlerFactoryTest, RegistryFactory) {
66 SSLInfo null_ssl_info;
67 HttpAuthHandlerRegistryFactory registry_factory(
68 /*http_auth_preferences=*/nullptr);
69 url::SchemeHostPort scheme_host_port(GURL("https://www.google.com"));
70 const int kBasicReturnCode = -1;
71 auto mock_factory_basic =
72 std::make_unique<MockHttpAuthHandlerFactory>(kBasicReturnCode);
73
74 const int kDigestReturnCode = -2;
75 auto mock_factory_digest =
76 std::make_unique<MockHttpAuthHandlerFactory>(kDigestReturnCode);
77
78 const int kDigestReturnCodeReplace = -3;
79 auto mock_factory_digest_replace =
80 std::make_unique<MockHttpAuthHandlerFactory>(kDigestReturnCodeReplace);
81
82 auto host_resovler = std::make_unique<MockHostResolver>();
83 std::unique_ptr<HttpAuthHandler> handler;
84
85 // No schemes should be supported in the beginning.
86 EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
87 registry_factory.CreateAuthHandlerFromString(
88 "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
89 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
90 host_resovler.get(), &handler));
91
92 // Test what happens with a single scheme.
93 registry_factory.RegisterSchemeFactory("Basic",
94 std::move(mock_factory_basic));
95 EXPECT_EQ(kBasicReturnCode,
96 registry_factory.CreateAuthHandlerFromString(
97 "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
98 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
99 host_resovler.get(), &handler));
100 EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
101 registry_factory.CreateAuthHandlerFromString(
102 "Digest", HttpAuth::AUTH_SERVER, null_ssl_info,
103 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
104 host_resovler.get(), &handler));
105
106 // Test multiple schemes
107 registry_factory.RegisterSchemeFactory("Digest",
108 std::move(mock_factory_digest));
109 EXPECT_EQ(kBasicReturnCode,
110 registry_factory.CreateAuthHandlerFromString(
111 "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
112 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
113 host_resovler.get(), &handler));
114 EXPECT_EQ(kDigestReturnCode,
115 registry_factory.CreateAuthHandlerFromString(
116 "Digest", HttpAuth::AUTH_SERVER, null_ssl_info,
117 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
118 host_resovler.get(), &handler));
119
120 // Test case-insensitivity
121 EXPECT_EQ(kBasicReturnCode,
122 registry_factory.CreateAuthHandlerFromString(
123 "basic", HttpAuth::AUTH_SERVER, null_ssl_info,
124 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
125 host_resovler.get(), &handler));
126
127 // Test replacement of existing auth scheme
128 registry_factory.RegisterSchemeFactory(
129 "Digest", std::move(mock_factory_digest_replace));
130 EXPECT_EQ(kBasicReturnCode,
131 registry_factory.CreateAuthHandlerFromString(
132 "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
133 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
134 host_resovler.get(), &handler));
135 EXPECT_EQ(kDigestReturnCodeReplace,
136 registry_factory.CreateAuthHandlerFromString(
137 "Digest", HttpAuth::AUTH_SERVER, null_ssl_info,
138 NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
139 host_resovler.get(), &handler));
140 }
141
TEST(HttpAuthHandlerFactoryTest,DefaultFactory)142 TEST(HttpAuthHandlerFactoryTest, DefaultFactory) {
143 auto host_resolver = std::make_unique<MockHostResolver>();
144 MockAllowHttpAuthPreferences http_auth_preferences;
145 std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
146 HttpAuthHandlerFactory::CreateDefault());
147 http_auth_handler_factory->SetHttpAuthPreferences(kNegotiateAuthScheme,
148 &http_auth_preferences);
149 url::SchemeHostPort server_scheme_host_port(GURL("http://www.example.com"));
150 url::SchemeHostPort proxy_scheme_host_port(
151 GURL("http://cache.example.com:3128"));
152 SSLInfo null_ssl_info;
153 {
154 std::unique_ptr<HttpAuthHandler> handler;
155 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
156 "Basic realm=\"FooBar\"", HttpAuth::AUTH_SERVER, null_ssl_info,
157 NetworkAnonymizationKey(), server_scheme_host_port, NetLogWithSource(),
158 host_resolver.get(), &handler);
159 EXPECT_THAT(rv, IsOk());
160 ASSERT_FALSE(handler.get() == nullptr);
161 EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, handler->auth_scheme());
162 EXPECT_STREQ("FooBar", handler->realm().c_str());
163 EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
164 EXPECT_FALSE(handler->encrypts_identity());
165 EXPECT_FALSE(handler->is_connection_based());
166 }
167 {
168 std::unique_ptr<HttpAuthHandler> handler;
169 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
170 "UNSUPPORTED realm=\"FooBar\"", HttpAuth::AUTH_SERVER, null_ssl_info,
171 NetworkAnonymizationKey(), server_scheme_host_port, NetLogWithSource(),
172 host_resolver.get(), &handler);
173 EXPECT_THAT(rv, IsError(ERR_UNSUPPORTED_AUTH_SCHEME));
174 EXPECT_TRUE(handler.get() == nullptr);
175 }
176 {
177 std::unique_ptr<HttpAuthHandler> handler;
178 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
179 "Digest realm=\"FooBar\", nonce=\"xyz\"", HttpAuth::AUTH_PROXY,
180 null_ssl_info, NetworkAnonymizationKey(), proxy_scheme_host_port,
181 NetLogWithSource(), host_resolver.get(), &handler);
182 EXPECT_THAT(rv, IsOk());
183 ASSERT_FALSE(handler.get() == nullptr);
184 EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, handler->auth_scheme());
185 EXPECT_STREQ("FooBar", handler->realm().c_str());
186 EXPECT_EQ(HttpAuth::AUTH_PROXY, handler->target());
187 EXPECT_TRUE(handler->encrypts_identity());
188 EXPECT_FALSE(handler->is_connection_based());
189 }
190 {
191 std::unique_ptr<HttpAuthHandler> handler;
192 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
193 "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, NetworkAnonymizationKey(),
194 server_scheme_host_port, NetLogWithSource(), host_resolver.get(),
195 &handler);
196 EXPECT_THAT(rv, IsOk());
197 ASSERT_FALSE(handler.get() == nullptr);
198 EXPECT_EQ(HttpAuth::AUTH_SCHEME_NTLM, handler->auth_scheme());
199 EXPECT_STREQ("", handler->realm().c_str());
200 EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
201 EXPECT_TRUE(handler->encrypts_identity());
202 EXPECT_TRUE(handler->is_connection_based());
203 }
204 {
205 std::unique_ptr<HttpAuthHandler> handler;
206 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
207 "Negotiate", HttpAuth::AUTH_SERVER, null_ssl_info,
208 NetworkAnonymizationKey(), server_scheme_host_port, NetLogWithSource(),
209 host_resolver.get(), &handler);
210 // Note the default factory doesn't support Kerberos on Android
211 #if BUILDFLAG(USE_KERBEROS) && !BUILDFLAG(IS_ANDROID)
212 EXPECT_THAT(rv, IsOk());
213 ASSERT_FALSE(handler.get() == nullptr);
214 EXPECT_EQ(HttpAuth::AUTH_SCHEME_NEGOTIATE, handler->auth_scheme());
215 EXPECT_STREQ("", handler->realm().c_str());
216 EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
217 EXPECT_TRUE(handler->encrypts_identity());
218 EXPECT_TRUE(handler->is_connection_based());
219 #else
220 EXPECT_THAT(rv, IsError(ERR_UNSUPPORTED_AUTH_SCHEME));
221 EXPECT_TRUE(handler.get() == nullptr);
222 #endif // BUILDFLAG(USE_KERBEROS) && !BUILDFLAG(IS_ANDROID)
223 }
224 }
225
TEST(HttpAuthHandlerFactoryTest,HttpAuthUrlFilter)226 TEST(HttpAuthHandlerFactoryTest, HttpAuthUrlFilter) {
227 auto host_resolver = std::make_unique<MockHostResolver>();
228
229 MockAllowHttpAuthPreferences http_auth_preferences;
230 // Set the Preference that blocks Basic Auth over HTTP on all of the
231 // factories. It shouldn't impact any behavior except for the Basic factory.
232 http_auth_preferences.set_basic_over_http_enabled(false);
233 // Set the preference that only allows "https://www.example.com" to use HTTP
234 // auth.
235 http_auth_preferences.set_http_auth_scheme_filter(
236 base::BindRepeating([](const url::SchemeHostPort& scheme_host_port) {
237 return scheme_host_port ==
238 url::SchemeHostPort(GURL("https://www.example.com"));
239 }));
240
241 std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
242 HttpAuthHandlerFactory::CreateDefault(&http_auth_preferences));
243
244 GURL nonsecure_origin("http://www.example.com");
245 GURL secure_origin("https://www.example.com");
246
247 SSLInfo null_ssl_info;
248 const HttpAuth::Target kTargets[] = {HttpAuth::AUTH_SERVER,
249 HttpAuth::AUTH_PROXY};
250 struct TestCase {
251 int expected_net_error;
252 const GURL origin;
253 const char* challenge;
254 } const kTestCases[] = {
255 {OK, secure_origin, "Basic realm=\"FooBar\""},
256 {ERR_UNSUPPORTED_AUTH_SCHEME, nonsecure_origin, "Basic realm=\"FooBar\""},
257 {OK, secure_origin, "Digest realm=\"FooBar\", nonce=\"xyz\""},
258 {OK, nonsecure_origin, "Digest realm=\"FooBar\", nonce=\"xyz\""},
259 {OK, secure_origin, "Ntlm"},
260 {OK, nonsecure_origin, "Ntlm"},
261 #if BUILDFLAG(USE_KERBEROS) && !BUILDFLAG(IS_ANDROID)
262 {OK, secure_origin, "Negotiate"},
263 {OK, nonsecure_origin, "Negotiate"},
264 #endif
265 };
266
267 for (const auto target : kTargets) {
268 for (const TestCase& test_case : kTestCases) {
269 std::unique_ptr<HttpAuthHandler> handler;
270 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
271 test_case.challenge, target, null_ssl_info, NetworkAnonymizationKey(),
272 url::SchemeHostPort(test_case.origin), NetLogWithSource(),
273 host_resolver.get(), &handler);
274 EXPECT_THAT(rv, IsError(test_case.expected_net_error));
275 }
276 }
277 }
278
TEST(HttpAuthHandlerFactoryTest,BasicFactoryRespectsHTTPEnabledPref)279 TEST(HttpAuthHandlerFactoryTest, BasicFactoryRespectsHTTPEnabledPref) {
280 auto host_resolver = std::make_unique<MockHostResolver>();
281 std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
282 HttpAuthHandlerFactory::CreateDefault());
283
284 // Set the Preference that blocks Basic Auth over HTTP on all of the
285 // factories. It shouldn't impact any behavior except for the Basic factory.
286 MockAllowHttpAuthPreferences http_auth_preferences;
287 http_auth_preferences.set_basic_over_http_enabled(false);
288 http_auth_handler_factory->SetHttpAuthPreferences(kBasicAuthScheme,
289 &http_auth_preferences);
290 http_auth_handler_factory->SetHttpAuthPreferences(kDigestAuthScheme,
291 &http_auth_preferences);
292 http_auth_handler_factory->SetHttpAuthPreferences(kNtlmAuthScheme,
293 &http_auth_preferences);
294 http_auth_handler_factory->SetHttpAuthPreferences(kNegotiateAuthScheme,
295 &http_auth_preferences);
296
297 url::SchemeHostPort nonsecure_scheme_host_port(
298 GURL("http://www.example.com"));
299 url::SchemeHostPort secure_scheme_host_port(GURL("https://www.example.com"));
300 SSLInfo null_ssl_info;
301
302 const HttpAuth::Target kTargets[] = {HttpAuth::AUTH_SERVER,
303 HttpAuth::AUTH_PROXY};
304 struct TestCase {
305 int expected_net_error;
306 const url::SchemeHostPort scheme_host_port;
307 const char* challenge;
308 } const kTestCases[] = {
309 // Challenges that result in success results.
310 {OK, secure_scheme_host_port, "Basic realm=\"FooBar\""},
311 {OK, secure_scheme_host_port, "Digest realm=\"FooBar\", nonce=\"xyz\""},
312 {OK, nonsecure_scheme_host_port, "Digest realm=\"FooBar\", nonce=\"xyz\""},
313 {OK, secure_scheme_host_port, "Ntlm"},
314 {OK, nonsecure_scheme_host_port, "Ntlm"},
315 #if BUILDFLAG(USE_KERBEROS) && !BUILDFLAG(IS_ANDROID)
316 {OK, secure_scheme_host_port, "Negotiate"},
317 {OK, nonsecure_scheme_host_port, "Negotiate"},
318 #endif
319 // Challenges that result in error results.
320 {ERR_UNSUPPORTED_AUTH_SCHEME, nonsecure_scheme_host_port,
321 "Basic realm=\"FooBar\""},
322 };
323
324 for (const auto target : kTargets) {
325 for (const TestCase& test_case : kTestCases) {
326 std::unique_ptr<HttpAuthHandler> handler;
327 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
328 test_case.challenge, target, null_ssl_info, NetworkAnonymizationKey(),
329 test_case.scheme_host_port, NetLogWithSource(), host_resolver.get(),
330 &handler);
331 EXPECT_THAT(rv, IsError(test_case.expected_net_error));
332 }
333 }
334 }
335
TEST(HttpAuthHandlerFactoryTest,LogCreateAuthHandlerResults)336 TEST(HttpAuthHandlerFactoryTest, LogCreateAuthHandlerResults) {
337 auto host_resolver = std::make_unique<MockHostResolver>();
338 std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
339 HttpAuthHandlerFactory::CreateDefault());
340 url::SchemeHostPort scheme_host_port(GURL("http://www.example.com"));
341 SSLInfo null_ssl_info;
342 RecordingNetLogObserver net_log_observer;
343
344 net::NetLogCaptureMode capture_modes[] = {
345 NetLogCaptureMode::kDefault, NetLogCaptureMode::kIncludeSensitive};
346
347 struct TestCase {
348 int expected_net_error;
349 const char* challenge;
350 const net::HttpAuth::Target auth_target;
351 const char* expected_scheme;
352 } test_cases[] = {
353 // Challenges that result in success results.
354 {OK, "Basic realm=\"FooBar\"", HttpAuth::AUTH_SERVER, "Basic"},
355 {OK, "Basic realm=\"FooBar\"", HttpAuth::AUTH_PROXY, "Basic"},
356 {OK, "Digest realm=\"FooBar\", nonce=\"xyz\"", HttpAuth::AUTH_SERVER,
357 "Digest"},
358 // Challenges that result in error results.
359 {ERR_INVALID_RESPONSE, "", HttpAuth::AUTH_SERVER, ""},
360 {ERR_INVALID_RESPONSE, "Digest realm=\"no_nonce\"", HttpAuth::AUTH_SERVER,
361 "Digest"},
362 {ERR_UNSUPPORTED_AUTH_SCHEME, "UNSUPPORTED realm=\"FooBar\"",
363 HttpAuth::AUTH_SERVER, "UNSUPPORTED"},
364 {ERR_UNSUPPORTED_AUTH_SCHEME, "invalid\xff\x0a", HttpAuth::AUTH_SERVER,
365 "%ESCAPED:\xE2\x80\x8B invalid%FF\n"},
366 {ERR_UNSUPPORTED_AUTH_SCHEME, "UNSUPPORTED2 realm=\"FooBar\"",
367 HttpAuth::AUTH_PROXY, "UNSUPPORTED2"}};
368
369 // For each level of capture sensitivity...
370 for (auto capture_mode : capture_modes) {
371 net_log_observer.SetObserverCaptureMode(capture_mode);
372
373 // ... evaluate the expected results for each test case.
374 for (auto test_case : test_cases) {
375 std::unique_ptr<HttpAuthHandler> handler;
376 int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
377 test_case.challenge, test_case.auth_target, null_ssl_info,
378 NetworkAnonymizationKey(), scheme_host_port,
379 NetLogWithSource::Make(NetLogSourceType::NONE), host_resolver.get(),
380 &handler);
381 EXPECT_THAT(rv, IsError(test_case.expected_net_error));
382 auto entries = net_log_observer.GetEntriesWithType(
383 NetLogEventType::AUTH_HANDLER_CREATE_RESULT);
384 ASSERT_EQ(1u, entries.size());
385 const std::string* scheme = entries[0].params.FindString("scheme");
386 ASSERT_NE(nullptr, scheme);
387 EXPECT_STRCASEEQ(test_case.expected_scheme, scheme->data());
388 std::optional<int> net_error = entries[0].params.FindInt("net_error");
389 if (test_case.expected_net_error) {
390 ASSERT_TRUE(net_error.has_value());
391 EXPECT_EQ(test_case.expected_net_error, net_error.value());
392 } else {
393 ASSERT_FALSE(net_error.has_value());
394 }
395
396 // The challenge should be logged only when sensitive logging is enabled.
397 const std::string* challenge = entries[0].params.FindString("challenge");
398 if (capture_mode == NetLogCaptureMode::kDefault) {
399 ASSERT_EQ(nullptr, challenge);
400 } else {
401 ASSERT_NE(nullptr, challenge);
402 EXPECT_EQ(net::NetLogStringValue(test_case.challenge).GetString(),
403 challenge->data());
404 }
405
406 net_log_observer.Clear();
407 }
408 }
409 }
410
411 } // namespace net
412