xref: /aosp_15_r20/external/cronet/net/http/http_auth_handler_factory_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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