1 // Copyright 2019 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 "base/containers/span.h"
6 #include "base/files/file_path.h"
7 #include "base/files/file_util.h"
8 #include "base/path_service.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/threading/scoped_blocking_call.h"
12 #include "build/build_config.h"
13 #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
14 #include "chrome/browser/ssl/cert_verifier_platform_browser_test.h"
15 #include "chrome/browser/ssl/ssl_browsertest_util.h"
16 #include "chrome/common/chrome_paths.h"
17 #include "chrome/test/base/chrome_test_utils.h"
18 #include "components/security_interstitials/core/controller_client.h"
19 #include "content/public/browser/navigation_controller.h"
20 #include "content/public/browser/navigation_entry.h"
21 #include "content/public/browser/network_service_instance.h"
22 #include "content/public/browser/ssl_status.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/browser_test.h"
25 #include "content/public/test/content_browser_test_utils.h"
26 #include "net/base/net_errors.h"
27 #include "net/cert/cert_status_flags.h"
28 #include "net/cert/cert_verify_result.h"
29 #include "net/cert/mock_cert_verifier.h"
30 #include "net/cert/x509_certificate.h"
31 #include "net/dns/mock_host_resolver.h"
32 #include "net/test/embedded_test_server/embedded_test_server.h"
33 #include "net/test/test_data_directory.h"
34
35 #if defined(OS_ANDROID)
36 #include "chrome/test/base/android/android_browser_test.h"
37 #else
38 #include "chrome/test/base/in_process_browser_test.h"
39 #endif
40
41 namespace AuthState = ssl_test_util::AuthState;
42 namespace CertError = ssl_test_util::CertError;
43
44 namespace {
45
46 class CRLSetBrowserTest : public PlatformBrowserTest {
47 public:
CRLSetBrowserTest()48 CRLSetBrowserTest() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
49 ~CRLSetBrowserTest() override = default;
50
SetUpOnMainThread()51 void SetUpOnMainThread() override {
52 PlatformBrowserTest::SetUpOnMainThread();
53
54 host_resolver()->AddRule("*", "127.0.0.1");
55
56 https_server_.ServeFilesFromSourceDirectory("chrome/test/data/");
57 }
58
59 protected:
GetActiveWebContents()60 content::WebContents* GetActiveWebContents() {
61 return chrome_test_utils::GetActiveWebContents(this);
62 }
63
64 net::EmbeddedTestServer https_server_;
65 };
66
67 const char kHstsTestHostName[] = "hsts-example.test";
68
69 } // namespace
70
IN_PROC_BROWSER_TEST_F(CRLSetBrowserTest,TestCRLSetRevoked)71 IN_PROC_BROWSER_TEST_F(CRLSetBrowserTest, TestCRLSetRevoked) {
72 ASSERT_TRUE(https_server_.Start());
73
74 std::string crl_set_bytes;
75 {
76 base::ScopedAllowBlockingForTesting allow_blocking;
77 base::ReadFileToString(
78 net::GetTestCertsDirectory().AppendASCII("crlset_by_leaf_spki.raw"),
79 &crl_set_bytes);
80 }
81 base::RunLoop run_loop;
82 content::GetNetworkService()->UpdateCRLSet(
83 base::as_bytes(base::make_span(crl_set_bytes)), run_loop.QuitClosure());
84 run_loop.Run();
85
86 bool interstitial_expected =
87 ssl_test_util::CertVerifierSupportsCRLSetBlocking();
88
89 // If an interstitial is expected, NavigateToURL should fail, because the
90 // interstitial will be the committed URL, rather than the navigated URL.
91 EXPECT_EQ(!interstitial_expected,
92 content::NavigateToURL(GetActiveWebContents(),
93 https_server_.GetURL("/ssl/google.html")));
94
95 ASSERT_EQ(interstitial_expected,
96 chrome_browser_interstitials::IsShowingInterstitial(
97 GetActiveWebContents()));
98 ASSERT_TRUE(WaitForRenderFrameReady(GetActiveWebContents()->GetMainFrame()));
99
100 if (interstitial_expected) {
101 ASSERT_TRUE(chrome_browser_interstitials::IsShowingSSLInterstitial(
102 GetActiveWebContents()));
103
104 ssl_test_util::CheckAuthenticationBrokenState(
105 GetActiveWebContents(), net::CERT_STATUS_REVOKED,
106 AuthState::SHOWING_INTERSTITIAL);
107 } else {
108 ssl_test_util::CheckAuthenticatedState(GetActiveWebContents(),
109 AuthState::NONE);
110 }
111 }
112
113 // Test that CRLSets configured to block MITM certificates cause the
114 // known interception interstitial.
IN_PROC_BROWSER_TEST_F(CRLSetBrowserTest,TestCRLSetBlockedInterception)115 IN_PROC_BROWSER_TEST_F(CRLSetBrowserTest, TestCRLSetBlockedInterception) {
116 ASSERT_TRUE(https_server_.Start());
117
118 // Load a CRLSet that marks the root as a blocked MITM.
119 std::string crl_set_bytes;
120 {
121 base::ScopedAllowBlockingForTesting allow_blocking;
122 base::ReadFileToString(net::GetTestCertsDirectory().AppendASCII(
123 "crlset_blocked_interception_by_root.raw"),
124 &crl_set_bytes);
125 }
126 base::RunLoop run_loop;
127 content::GetNetworkService()->UpdateCRLSet(
128 base::as_bytes(base::make_span(crl_set_bytes)), run_loop.QuitClosure());
129 run_loop.Run();
130
131 bool interstitial_expected =
132 ssl_test_util::CertVerifierSupportsCRLSetBlocking();
133
134 // If an interstitial is expected, NavigateToURL should fail, because the
135 // interstitial will be the committed URL, rather than the navigated URL.
136 EXPECT_EQ(!interstitial_expected,
137 content::NavigateToURL(GetActiveWebContents(),
138 https_server_.GetURL("/ssl/google.html")));
139
140 ASSERT_EQ(interstitial_expected,
141 chrome_browser_interstitials::IsShowingInterstitial(
142 GetActiveWebContents()));
143 ASSERT_TRUE(WaitForRenderFrameReady(GetActiveWebContents()->GetMainFrame()));
144
145 if (interstitial_expected) {
146 ASSERT_TRUE(
147 chrome_browser_interstitials::IsShowingBlockedInterceptionInterstitial(
148 GetActiveWebContents()));
149
150 ssl_test_util::CheckAuthenticationBrokenState(
151 GetActiveWebContents(),
152 net::CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED | net::CERT_STATUS_REVOKED,
153 AuthState::SHOWING_INTERSTITIAL);
154 } else {
155 ssl_test_util::CheckAuthenticatedState(GetActiveWebContents(),
156 AuthState::NONE);
157 }
158 }
159
160 // Test that CRLSets configured to identify known MITM certificates do not
161 // cause an interstitial unless the MITM certificate is blocked.
IN_PROC_BROWSER_TEST_F(CRLSetBrowserTest,TestCRLSetKnownInterception)162 IN_PROC_BROWSER_TEST_F(CRLSetBrowserTest, TestCRLSetKnownInterception) {
163 ASSERT_TRUE(https_server_.Start());
164
165 // Load a CRLSet that marks the root as a known MITM.
166 std::string crl_set_bytes;
167 {
168 base::ScopedAllowBlockingForTesting allow_blocking;
169 base::ReadFileToString(net::GetTestCertsDirectory().AppendASCII(
170 "crlset_known_interception_by_root.raw"),
171 &crl_set_bytes);
172 }
173 base::RunLoop run_loop;
174 content::GetNetworkService()->UpdateCRLSet(
175 base::as_bytes(base::make_span(crl_set_bytes)), run_loop.QuitClosure());
176 run_loop.Run();
177
178 // Navigate to the page. It should not cause an interstitial, but should
179 // allow for the display of additional information that interception is
180 // happening.
181 ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
182 https_server_.GetURL("/ssl/google.html")));
183 ssl_test_util::CheckAuthenticatedState(GetActiveWebContents(),
184 AuthState::NONE);
185
186 content::NavigationEntry* entry =
187 GetActiveWebContents()->GetController().GetVisibleEntry();
188 ASSERT_TRUE(entry);
189 EXPECT_TRUE(entry->GetSSL().cert_status &
190 net::CERT_STATUS_KNOWN_INTERCEPTION_DETECTED);
191 }
192
193 // While TestCRLSetBlockedInterception and TestCRLSetKnownInterception use
194 // a real CertVerifier in order to test that a real CRLSet is delivered and
195 // processed, testing HSTS requires with a MockCertVerifier so that the
196 // cert will match the intended hostname, and thus only fail because it's a
197 // blocked MITM certificate. This requires using a CertVerifierBrowserTest,
198 // which is not suitable for the previous tests because it does not test
199 // CRLSets.
200 class CRLSetInterceptionBrowserTest : public CertVerifierPlatformBrowserTest {
201 public:
CRLSetInterceptionBrowserTest()202 CRLSetInterceptionBrowserTest()
203 : CertVerifierPlatformBrowserTest(),
204 https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
205
SetUpOnMainThread()206 void SetUpOnMainThread() override {
207 CertVerifierPlatformBrowserTest::SetUpOnMainThread();
208 ssl_test_util::SetHSTSForHostName(
209 GetActiveWebContents()->GetBrowserContext(), kHstsTestHostName);
210
211 host_resolver()->AddRule("*", "127.0.0.1");
212 https_server_.ServeFilesFromSourceDirectory("chrome/test/data/");
213 }
214
215 protected:
GetActiveWebContents()216 content::WebContents* GetActiveWebContents() {
217 return chrome_test_utils::GetActiveWebContents(this);
218 }
219
220 net::EmbeddedTestServer https_server_;
221 };
222
IN_PROC_BROWSER_TEST_F(CRLSetInterceptionBrowserTest,KnownInterceptionWorksOnHSTS)223 IN_PROC_BROWSER_TEST_F(CRLSetInterceptionBrowserTest,
224 KnownInterceptionWorksOnHSTS) {
225 ASSERT_TRUE(https_server_.Start());
226
227 net::CertVerifyResult verify_result;
228 verify_result.verified_cert = https_server_.GetCertificate();
229 verify_result.cert_status =
230 net::CERT_STATUS_REVOKED | net::CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED;
231
232 // Simulate verification returning that the certificate is a blocked
233 // interception certificate.
234 mock_cert_verifier()->AddResultForCertAndHost(
235 https_server_.GetCertificate(), kHstsTestHostName, verify_result,
236 net::ERR_CERT_KNOWN_INTERCEPTION_BLOCKED);
237
238 GURL::Replacements replacements;
239 replacements.SetHostStr(kHstsTestHostName);
240 GURL url =
241 https_server_.GetURL("/ssl/google.html").ReplaceComponents(replacements);
242
243 // Expect navigation to fail, because this should always result in an
244 // interstitial.
245 ASSERT_FALSE(content::NavigateToURL(GetActiveWebContents(), url));
246
247 ASSERT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
248 GetActiveWebContents()));
249 ASSERT_TRUE(WaitForRenderFrameReady(GetActiveWebContents()->GetMainFrame()));
250
251 ssl_test_util::CheckSecurityState(
252 GetActiveWebContents(),
253 net::CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED | net::CERT_STATUS_REVOKED,
254 security_state::DANGEROUS, AuthState::SHOWING_INTERSTITIAL);
255
256 ASSERT_TRUE(
257 chrome_browser_interstitials::IsShowingBlockedInterceptionInterstitial(
258 GetActiveWebContents()));
259
260 // Expect there to be a proceed link, even for HSTS.
261 EXPECT_TRUE(chrome_browser_interstitials::InterstitialHasProceedLink(
262 GetActiveWebContents()->GetMainFrame()));
263 }
264