1 // Copyright 2020 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 "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_web_request_service.h"
6 
7 #include <map>
8 #include <memory>
9 
10 #include "base/bind.h"
11 #include "base/macros.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/run_loop.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chromeos/wilco_dtc_supportd/mojo_utils.h"
16 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_network_context.h"
17 #include "chrome/browser/net/system_network_context_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
21 #include "chrome/test/base/in_process_browser_test.h"
22 #include "content/public/test/browser_test.h"
23 #include "content/public/test/simple_url_loader_test_helper.h"
24 #include "net/cookies/site_for_cookies.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h"
28 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
29 #include "services/network/public/cpp/simple_url_loader.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "url/gurl.h"
32 
33 namespace chromeos {
34 
35 namespace {
36 
37 // Performs a request once using WilcoDctSupportdWebRequestService, waiting for
38 // the result to be available.
39 class ServiceRequestPerformer {
40  public:
ServiceRequestPerformer(WilcoDtcSupportdWebRequestService * web_request_service)41   explicit ServiceRequestPerformer(
42       WilcoDtcSupportdWebRequestService* web_request_service)
43       : web_request_service_(web_request_service) {
44     DCHECK(web_request_service_);
45   }
46 
47   ~ServiceRequestPerformer() = default;
48 
49   ServiceRequestPerformer(const ServiceRequestPerformer&) = delete;
50   ServiceRequestPerformer& operator=(const ServiceRequestPerformer&) = delete;
51 
PerformRequest(const GURL & url)52   void PerformRequest(const GURL& url) {
53     ASSERT_FALSE(request_performed_);
54 
55     request_performed_ = true;
56 
57     base::RunLoop run_loop;
58     web_request_service_->PerformRequest(
59         wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestHttpMethod::kGet,
60         url, {}, "",
61         base::BindOnce(
62             [](base::OnceClosure callback,
63                wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus*
64                    out_status,
65                int* out_response, mojo::ScopedHandle* out_response_handle,
66                wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus
67                    status,
68                int response, mojo::ScopedHandle response_handle) {
69               *out_status = status;
70               *out_response = response;
71               *out_response_handle = std::move(response_handle);
72               std::move(callback).Run();
73             },
74             run_loop.QuitClosure(), &status_, &response_, &response_handle_));
75     run_loop.Run();
76   }
77 
status() const78   wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus status() const {
79     DCHECK(request_performed_);
80     return status_;
81   }
82 
response() const83   int response() const {
84     DCHECK(request_performed_);
85     return response_;
86   }
87 
response_body_release()88   std::string response_body_release() {
89     DCHECK(request_performed_);
90     base::ReadOnlySharedMemoryMapping response_shared_memory;
91     return MojoUtils::GetStringPieceFromMojoHandle(std::move(response_handle_),
92                                                    &response_shared_memory)
93         .as_string();
94   }
95 
96  private:
97   bool request_performed_ = false;
98 
99   // Not owned.
100   WilcoDtcSupportdWebRequestService* const web_request_service_;
101 
102   // Results of the request:
103   wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus status_ =
104       wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk;
105   int response_ = 0;
106   mojo::ScopedHandle response_handle_;
107 };
108 
109 // Performs a request once using network::mojom::URLLoaderFactory, waiting for
110 // the result to be available.
111 class ContextRequestPerformer {
112  public:
ContextRequestPerformer(network::mojom::URLLoaderFactory * url_loader_factory)113   explicit ContextRequestPerformer(
114       network::mojom::URLLoaderFactory* url_loader_factory)
115       : url_loader_factory_(url_loader_factory) {
116     DCHECK(url_loader_factory_);
117   }
118 
119   ~ContextRequestPerformer() = default;
120 
121   ContextRequestPerformer(const ContextRequestPerformer&) = delete;
122   ContextRequestPerformer& operator=(const ContextRequestPerformer&) = delete;
123 
PerformRequest(const GURL & url)124   void PerformRequest(const GURL& url) {
125     ASSERT_FALSE(request_performed_);
126     request_performed_ = true;
127 
128     auto request = std::make_unique<network::ResourceRequest>();
129     request->url = url;
130     // Allow access to SameSite cookies.
131     request->site_for_cookies = net::SiteForCookies::FromUrl(url);
132 
133     auto url_loader = network::SimpleURLLoader::Create(
134         std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
135 
136     url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
137         url_loader_factory_, url_loader_test_helper_.GetCallback());
138     url_loader_test_helper_.WaitForCallback();
139   }
140 
response_body() const141   const std::string* response_body() const {
142     DCHECK(request_performed_);
143     return url_loader_test_helper_.response_body();
144   }
145 
146  private:
147   bool request_performed_ = false;
148 
149   network::mojom::URLLoaderFactory* const url_loader_factory_;
150 
151   content::SimpleURLLoaderTestHelper url_loader_test_helper_;
152 };
153 
154 }  // namespace
155 
156 class WilcoDtcSupportdNetworkContextTest : public InProcessBrowserTest {
157  public:
WilcoDtcSupportdNetworkContextTest()158   WilcoDtcSupportdNetworkContextTest()
159       : embedded_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
160 
161   ~WilcoDtcSupportdNetworkContextTest() override = default;
162 
163   WilcoDtcSupportdNetworkContextTest(
164       const WilcoDtcSupportdNetworkContextTest&) = delete;
165   WilcoDtcSupportdNetworkContextTest& operator=(
166       const WilcoDtcSupportdNetworkContextTest&) = delete;
167 
SetUpOnMainThread()168   void SetUpOnMainThread() override {
169     auto network_context_impl =
170         std::make_unique<WilcoDtcSupportdNetworkContextImpl>();
171     network_context_impl_ptr_ = network_context_impl.get();
172     web_request_service_ = std::make_unique<WilcoDtcSupportdWebRequestService>(
173         std::move(network_context_impl));
174   }
175 
TearDownOnMainThread()176   void TearDownOnMainThread() override {
177     network_context_impl_ptr_ = nullptr;
178     web_request_service_.reset();
179   }
180 
StartServer()181   void StartServer() {
182     embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir());
183     ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
184     embedded_test_server()->StartAcceptingConnections();
185   }
186 
embedded_test_server()187   net::EmbeddedTestServer* embedded_test_server() {
188     return &embedded_test_server_;
189   }
190 
network_context_impl()191   WilcoDtcSupportdNetworkContextImpl* network_context_impl() {
192     DCHECK(network_context_impl_ptr_);
193     return network_context_impl_ptr_;
194   }
195 
web_request_service()196   WilcoDtcSupportdWebRequestService* web_request_service() {
197     DCHECK(web_request_service_);
198     return web_request_service_.get();
199   }
200 
201  private:
202   net::EmbeddedTestServer embedded_test_server_;
203 
204   // Owned by |web_request_service_|.
205   WilcoDtcSupportdNetworkContextImpl* network_context_impl_ptr_ = nullptr;
206 
207   std::unique_ptr<WilcoDtcSupportdWebRequestService> web_request_service_;
208 };
209 
210 // Verifies that by default it's not allowed to perform web requests to the
211 // local host.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,LocalHostRequestsAreNotAllowed)212 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
213                        LocalHostRequestsAreNotAllowed) {
214   ASSERT_NO_FATAL_FAILURE(StartServer());
215 
216   {
217     ServiceRequestPerformer request_performer(web_request_service());
218     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
219         embedded_test_server()->GetURL("/echo")));
220     EXPECT_EQ(request_performer.status(),
221               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::
222                   kNetworkError);
223     EXPECT_EQ(request_performer.response(), 0);
224     EXPECT_EQ(request_performer.response_body_release(), "");
225   }
226 }
227 
228 // Verifies that client receives non-empty body in case of HTTP error.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NonEmpyResponseBodyOnNetworkError)229 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
230                        NonEmpyResponseBodyOnNetworkError) {
231   ASSERT_NO_FATAL_FAILURE(StartServer());
232 
233   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
234 
235   {
236     ServiceRequestPerformer request_performer(web_request_service());
237     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
238         embedded_test_server()->GetURL("/echo?status=500")));
239     EXPECT_EQ(request_performer.status(),
240               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::
241                   kHttpError);
242     EXPECT_EQ(request_performer.response(), 500);
243     EXPECT_EQ(request_performer.response_body_release(), "Echo");
244   }
245 }
246 
247 // Verifies that client continues request if client SSL certifient was not
248 // requested by server.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NoClientCertificate)249 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
250                        NoClientCertificate) {
251   ASSERT_NO_FATAL_FAILURE(StartServer());
252 
253   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
254 
255   {
256     ServiceRequestPerformer request_performer(web_request_service());
257     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
258         embedded_test_server()->GetURL("/echo")));
259     EXPECT_EQ(request_performer.status(),
260               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
261     EXPECT_EQ(request_performer.response(), 200);
262     EXPECT_EQ(request_performer.response_body_release(), "Echo");
263   }
264 }
265 
266 // Verifies that client continues request if client SSL certifient was
267 // requested by server as optional.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,OptionalClientCertificate)268 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
269                        OptionalClientCertificate) {
270   net::SSLServerConfig ssl_server_config;
271   ssl_server_config.client_cert_type =
272       net::SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT;
273   embedded_test_server()->SetSSLConfig(
274       net::EmbeddedTestServer::ServerCertificate::CERT_OK, ssl_server_config);
275 
276   ASSERT_NO_FATAL_FAILURE(StartServer());
277 
278   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
279 
280   {
281     ServiceRequestPerformer request_performer(web_request_service());
282     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
283         embedded_test_server()->GetURL("/echo")));
284     EXPECT_EQ(request_performer.status(),
285               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
286     EXPECT_EQ(request_performer.response(), 200);
287     EXPECT_EQ(request_performer.response_body_release(), "Echo");
288   }
289 }
290 
291 // Verifies that client does not continue request if client SSL certifient was
292 // requested by server as required.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,RequireClientCertificate)293 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
294                        RequireClientCertificate) {
295   net::SSLServerConfig ssl_server_config;
296   ssl_server_config.client_cert_type =
297       net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT;
298   embedded_test_server()->SetSSLConfig(
299       net::EmbeddedTestServer::ServerCertificate::CERT_OK, ssl_server_config);
300 
301   ASSERT_NO_FATAL_FAILURE(StartServer());
302 
303   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
304 
305   {
306     ServiceRequestPerformer request_performer(web_request_service());
307     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
308         embedded_test_server()->GetURL("/echo")));
309     EXPECT_EQ(request_performer.status(),
310               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::
311                   kNetworkError);
312     EXPECT_EQ(request_performer.response(), 0);
313     EXPECT_EQ(request_performer.response_body_release(), "");
314   }
315 }
316 
317 // Verifies that WilcoDtcSupportdNetworkContext reuses valid connection to
318 // networking service.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,ReuseValidNetworkingServiceConnection)319 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
320                        ReuseValidNetworkingServiceConnection) {
321   ASSERT_NO_FATAL_FAILURE(StartServer());
322 
323   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
324 
325   {
326     ServiceRequestPerformer request_performer(web_request_service());
327     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
328         embedded_test_server()->GetURL("/echo")));
329     EXPECT_EQ(request_performer.status(),
330               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
331     EXPECT_EQ(request_performer.response(), 200);
332     EXPECT_EQ(request_performer.response_body_release(), "Echo");
333   }
334 
335   {
336     ServiceRequestPerformer request_performer(web_request_service());
337     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
338         embedded_test_server()->GetURL("/echo")));
339     EXPECT_EQ(request_performer.status(),
340               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
341     EXPECT_EQ(request_performer.response(), 200);
342     EXPECT_EQ(request_performer.response_body_release(), "Echo");
343   }
344 }
345 
346 // Verifies that WilcoDtcSupportdNetworkContext reconnects to network service
347 // on network service crash.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,CrashNetworkingService)348 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
349                        CrashNetworkingService) {
350   ASSERT_NO_FATAL_FAILURE(StartServer());
351 
352   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
353 
354   {
355     ServiceRequestPerformer request_performer(web_request_service());
356     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
357         embedded_test_server()->GetURL("/echo")));
358     EXPECT_EQ(request_performer.status(),
359               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
360     EXPECT_EQ(request_performer.response(), 200);
361     EXPECT_EQ(request_performer.response_body_release(), "Echo");
362   }
363 
364   SimulateNetworkServiceCrash();
365   network_context_impl()->FlushForTesting();
366 
367   {
368     ServiceRequestPerformer request_performer(web_request_service());
369     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
370         embedded_test_server()->GetURL("/echo")));
371     EXPECT_EQ(request_performer.status(),
372               wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
373     EXPECT_EQ(request_performer.response(), 200);
374     EXPECT_EQ(request_performer.response_body_release(), "Echo");
375   }
376 }
377 
378 // Verifies that WilcoDtcSupportdWebRequestService neither saves nor sends
379 // cookies.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NoCookies)380 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest, NoCookies) {
381   ASSERT_NO_FATAL_FAILURE(StartServer());
382 
383   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
384 
385   // Verify that wilco network context neither sends nor saves any cookies.
386   {
387     ServiceRequestPerformer request_performer(web_request_service());
388     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
389         embedded_test_server()->GetURL("/echoheader?cookie")));
390     EXPECT_EQ(request_performer.response_body_release(), "None");
391   }
392   {
393     const std::string kCookies = "foo=bar";
394 
395     ServiceRequestPerformer request_performer(web_request_service());
396     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
397         embedded_test_server()->GetURL("/set-cookie?" + kCookies)));
398     EXPECT_EQ(request_performer.response_body_release(), kCookies);
399   }
400   {
401     ServiceRequestPerformer request_performer(web_request_service());
402     ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
403         embedded_test_server()->GetURL("/echoheader?cookie")));
404     EXPECT_EQ(request_performer.response_body_release(), "None");
405   }
406 }
407 
408 // Verifies that WilcoDtcSupportdNetworkContextImpl neither reads nor saves
409 // cookies in non wilco network contexts.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NotUsingOtherNetworkContexts)410 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
411                        NotUsingOtherNetworkContexts) {
412   ASSERT_NO_FATAL_FAILURE(StartServer());
413 
414   web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
415 
416   const std::map<std::string, network::mojom::URLLoaderFactory*>
417       url_loader_factories{
418           {"profile", browser()->profile()->GetURLLoaderFactory().get()},
419           {"system", g_browser_process->system_network_context_manager()
420                          ->GetSharedURLLoaderFactory()
421                          .get()}};
422 
423   for (const auto& iter : url_loader_factories) {
424     LOG(INFO)
425         << "Verifying that wilco web request service does not use cookies from "
426         << iter.first << " network context.";
427 
428     const std::string kCookiesFooBar = "foo=bar";
429 
430     // Setup foo=bar cookies for non wilco network context.
431     {
432       ContextRequestPerformer context_request_performer(iter.second);
433       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
434           embedded_test_server()->GetURL("/echoheader?cookie")));
435       ASSERT_TRUE(context_request_performer.response_body());
436       EXPECT_EQ(*context_request_performer.response_body(), "None");
437     }
438     {
439       ContextRequestPerformer context_request_performer(iter.second);
440       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
441           embedded_test_server()->GetURL("/set-cookie?" + kCookiesFooBar)));
442       ASSERT_TRUE(context_request_performer.response_body());
443       EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
444     }
445     {
446       ContextRequestPerformer context_request_performer(iter.second);
447       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
448           embedded_test_server()->GetURL("/echoheader?cookie")));
449       ASSERT_TRUE(context_request_performer.response_body());
450       EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
451     }
452 
453     const std::string kCookiesBarFoo = "bar=foo";
454     WilcoDtcSupportdNetworkContextImpl wilco_network_context;
455 
456     // Verify that wilco network context neither reads nor saves cookies in
457     // non wilco network context.
458     {
459       ContextRequestPerformer context_request_performer(
460           wilco_network_context.GetURLLoaderFactory());
461       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
462           embedded_test_server()->GetURL("/echoheader?cookie")));
463       EXPECT_EQ(*context_request_performer.response_body(), "None");
464     }
465     {
466       ContextRequestPerformer context_request_performer(
467           wilco_network_context.GetURLLoaderFactory());
468       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
469           embedded_test_server()->GetURL("/set-cookie?" + kCookiesBarFoo)));
470       EXPECT_EQ(*context_request_performer.response_body(), kCookiesBarFoo);
471     }
472     {
473       ContextRequestPerformer context_request_performer(
474           wilco_network_context.GetURLLoaderFactory());
475       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
476           embedded_test_server()->GetURL("/echoheader?cookie")));
477       EXPECT_EQ(*context_request_performer.response_body(), kCookiesBarFoo);
478     }
479 
480     // Verify that we still have foo=bar cookies for non wilco network context.
481     {
482       ContextRequestPerformer context_request_performer(iter.second);
483       ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
484           embedded_test_server()->GetURL("/echoheader?cookie")));
485       ASSERT_TRUE(context_request_performer.response_body());
486       EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
487     }
488   }
489 }
490 
491 }  // namespace chromeos
492