1 // Copyright 2012 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/cert/multi_threaded_cert_verifier.h"
6
7 #include <memory>
8
9 #include "base/debug/leak_annotations.h"
10 #include "base/files/file_path.h"
11 #include "base/format_macros.h"
12 #include "base/functional/bind.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/test_completion_callback.h"
15 #include "net/cert/cert_verify_proc.h"
16 #include "net/cert/cert_verify_result.h"
17 #include "net/cert/crl_set.h"
18 #include "net/cert/mock_cert_verifier.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/log/net_log_with_source.h"
21 #include "net/test/cert_test_util.h"
22 #include "net/test/gtest_util.h"
23 #include "net/test/test_data_directory.h"
24 #include "net/test/test_with_task_environment.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 using net::test::IsError;
29 using net::test::IsOk;
30 using testing::_;
31 using testing::DoAll;
32 using testing::Return;
33
34 namespace net {
35
36 class ChromeRootStoreData;
37 class CertNetFetcher;
38
39 namespace {
40
FailTest(int)41 void FailTest(int /* result */) {
42 FAIL();
43 }
44
45 class MockCertVerifyProc : public CertVerifyProc {
46 public:
MockCertVerifyProc()47 MockCertVerifyProc() : CertVerifyProc(CRLSet::BuiltinCRLSet()) {}
48 MOCK_METHOD7(VerifyInternal,
49 int(X509Certificate*,
50 const std::string&,
51 const std::string&,
52 const std::string&,
53 int,
54 CertVerifyResult*,
55 const NetLogWithSource&));
56 MOCK_CONST_METHOD0(SupportsAdditionalTrustAnchors, bool());
57
58 private:
59 ~MockCertVerifyProc() override = default;
60 };
61
ACTION(SetCertVerifyResult)62 ACTION(SetCertVerifyResult) {
63 X509Certificate* cert = arg0;
64 CertVerifyResult* result = arg5;
65 result->Reset();
66 result->verified_cert = cert;
67 result->cert_status = CERT_STATUS_COMMON_NAME_INVALID;
68 }
69
ACTION(SetCertVerifyRevokedResult)70 ACTION(SetCertVerifyRevokedResult) {
71 X509Certificate* cert = arg0;
72 CertVerifyResult* result = arg5;
73 result->Reset();
74 result->verified_cert = cert;
75 result->cert_status = CERT_STATUS_REVOKED;
76 }
77
78 class SwapWithNewProcFactory : public CertVerifyProcFactory {
79 public:
SwapWithNewProcFactory(scoped_refptr<CertVerifyProc> new_mock_proc)80 explicit SwapWithNewProcFactory(scoped_refptr<CertVerifyProc> new_mock_proc)
81 : mock_verify_proc_(std::move(new_mock_proc)) {}
82
CreateCertVerifyProc(scoped_refptr<CertNetFetcher> cert_net_fetcher,const CertVerifyProc::ImplParams & impl_params,const CertVerifyProc::InstanceParams & instance_params)83 scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc(
84 scoped_refptr<CertNetFetcher> cert_net_fetcher,
85 const CertVerifyProc::ImplParams& impl_params,
86 const CertVerifyProc::InstanceParams& instance_params) override {
87 return mock_verify_proc_;
88 }
89
90 protected:
91 ~SwapWithNewProcFactory() override = default;
92 scoped_refptr<CertVerifyProc> mock_verify_proc_;
93 };
94
95 } // namespace
96
97 class MultiThreadedCertVerifierTest : public TestWithTaskEnvironment {
98 public:
MultiThreadedCertVerifierTest()99 MultiThreadedCertVerifierTest()
100 : mock_verify_proc_(base::MakeRefCounted<MockCertVerifyProc>()),
101 mock_new_verify_proc_(base::MakeRefCounted<MockCertVerifyProc>()),
102 verifier_(std::make_unique<MultiThreadedCertVerifier>(
103 mock_verify_proc_,
104 base::MakeRefCounted<SwapWithNewProcFactory>(
105 mock_new_verify_proc_))) {
106 EXPECT_CALL(*mock_verify_proc_, SupportsAdditionalTrustAnchors())
107 .WillRepeatedly(Return(true));
108 EXPECT_CALL(*mock_verify_proc_, VerifyInternal(_, _, _, _, _, _, _))
109 .WillRepeatedly(
110 DoAll(SetCertVerifyResult(), Return(ERR_CERT_COMMON_NAME_INVALID)));
111 }
112 ~MultiThreadedCertVerifierTest() override = default;
113
114 protected:
115 scoped_refptr<MockCertVerifyProc> mock_verify_proc_;
116 // The new verify_proc_ swapped in if the proc is updated.
117 scoped_refptr<MockCertVerifyProc> mock_new_verify_proc_;
118 std::unique_ptr<MultiThreadedCertVerifier> verifier_;
119 };
120
121 // Tests that the callback of a canceled request is never made.
TEST_F(MultiThreadedCertVerifierTest,CancelRequest)122 TEST_F(MultiThreadedCertVerifierTest, CancelRequest) {
123 base::FilePath certs_dir = GetTestCertsDirectory();
124 scoped_refptr<X509Certificate> test_cert(
125 ImportCertFromFile(certs_dir, "ok_cert.pem"));
126 ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
127
128 int error;
129 CertVerifyResult verify_result;
130 std::unique_ptr<CertVerifier::Request> request;
131
132 error = verifier_->Verify(
133 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
134 /*ocsp_response=*/std::string(),
135 /*sct_list=*/std::string()),
136 &verify_result, base::BindOnce(&FailTest), &request, NetLogWithSource());
137 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
138 ASSERT_TRUE(request);
139 request.reset();
140
141 // Issue a few more requests to the worker pool and wait for their
142 // completion, so that the task of the canceled request (which runs on a
143 // worker thread) is likely to complete by the end of this test.
144 TestCompletionCallback callback;
145 for (int i = 0; i < 5; ++i) {
146 error = verifier_->Verify(
147 CertVerifier::RequestParams(test_cert, "www2.example.com", 0,
148 /*ocsp_response=*/std::string(),
149 /*sct_list=*/std::string()),
150 &verify_result, callback.callback(), &request, NetLogWithSource());
151 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
152 EXPECT_TRUE(request);
153 error = callback.WaitForResult();
154 }
155 }
156
157 // Tests that the callback of a request is never made if the |verifier_| itself
158 // is deleted.
TEST_F(MultiThreadedCertVerifierTest,DeleteVerifier)159 TEST_F(MultiThreadedCertVerifierTest, DeleteVerifier) {
160 base::FilePath certs_dir = GetTestCertsDirectory();
161 scoped_refptr<X509Certificate> test_cert(
162 ImportCertFromFile(certs_dir, "ok_cert.pem"));
163 ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
164
165 int error;
166 CertVerifyResult verify_result;
167 std::unique_ptr<CertVerifier::Request> request;
168
169 error = verifier_->Verify(
170 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
171 /*ocsp_response=*/std::string(),
172 /*sct_list=*/std::string()),
173 &verify_result, base::BindOnce(&FailTest), &request, NetLogWithSource());
174 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
175 ASSERT_TRUE(request);
176 verifier_.reset();
177
178 RunUntilIdle();
179 }
180
181 namespace {
182
183 struct CertVerifyResultHelper {
FailTestnet::__anonfe15b6840211::CertVerifyResultHelper184 void FailTest(int /* result */) { FAIL(); }
185 std::unique_ptr<CertVerifier::Request> request;
186 };
187
188 } // namespace
189
190 // The same as the above "DeleteVerifier" test, except the callback provided
191 // will own the CertVerifier::Request as allowed by the CertVerifier contract.
192 // This is a regression test for https://crbug.com/1157562.
TEST_F(MultiThreadedCertVerifierTest,DeleteVerifierCallbackOwnsResult)193 TEST_F(MultiThreadedCertVerifierTest, DeleteVerifierCallbackOwnsResult) {
194 base::FilePath certs_dir = GetTestCertsDirectory();
195 scoped_refptr<X509Certificate> test_cert(
196 ImportCertFromFile(certs_dir, "ok_cert.pem"));
197 ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
198
199 int error;
200 CertVerifyResult verify_result;
201 std::unique_ptr<CertVerifyResultHelper> result_helper =
202 std::make_unique<CertVerifyResultHelper>();
203 CertVerifyResultHelper* result_helper_ptr = result_helper.get();
204 CompletionOnceCallback callback = base::BindOnce(
205 &CertVerifyResultHelper::FailTest, std::move(result_helper));
206
207 error = verifier_->Verify(
208 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
209 /*ocsp_response=*/std::string(),
210 /*sct_list=*/std::string()),
211 &verify_result, std::move(callback), &result_helper_ptr->request,
212 NetLogWithSource());
213 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
214 ASSERT_TRUE(result_helper_ptr->request);
215 verifier_.reset();
216
217 RunUntilIdle();
218 }
219
220 // Tests that a canceled request is not leaked.
TEST_F(MultiThreadedCertVerifierTest,CancelRequestThenQuit)221 TEST_F(MultiThreadedCertVerifierTest, CancelRequestThenQuit) {
222 base::FilePath certs_dir = GetTestCertsDirectory();
223 scoped_refptr<X509Certificate> test_cert(
224 ImportCertFromFile(certs_dir, "ok_cert.pem"));
225 ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
226
227 int error;
228 CertVerifyResult verify_result;
229 TestCompletionCallback callback;
230 std::unique_ptr<CertVerifier::Request> request;
231
232 {
233 // Because shutdown intentionally doesn't join worker threads, memory may
234 // be leaked if the main thread shuts down before the worker thread
235 // completes. In particular MultiThreadedCertVerifier calls
236 // base::WorkerPool::PostTaskAndReply(), which leaks its "relay" when it
237 // can't post the reply back to the origin thread. See
238 // https://crbug.com/522514
239 ANNOTATE_SCOPED_MEMORY_LEAK;
240 error = verifier_->Verify(
241 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
242 /*ocsp_response=*/std::string(),
243 /*sct_list=*/std::string()),
244 &verify_result, callback.callback(), &request, NetLogWithSource());
245 }
246 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
247 EXPECT_TRUE(request);
248 request.reset();
249 // Destroy |verifier_| by going out of scope.
250 }
251
252 // Tests propagation of configuration options into CertVerifyProc flags
TEST_F(MultiThreadedCertVerifierTest,ConvertsConfigToFlags)253 TEST_F(MultiThreadedCertVerifierTest, ConvertsConfigToFlags) {
254 base::FilePath certs_dir = GetTestCertsDirectory();
255 scoped_refptr<X509Certificate> test_cert(
256 ImportCertFromFile(certs_dir, "ok_cert.pem"));
257 ASSERT_TRUE(test_cert);
258
259 const struct TestConfig {
260 bool CertVerifier::Config::*config_ptr;
261 int expected_flag;
262 } kTestConfig[] = {
263 {&CertVerifier::Config::enable_rev_checking,
264 CertVerifyProc::VERIFY_REV_CHECKING_ENABLED},
265 {&CertVerifier::Config::require_rev_checking_local_anchors,
266 CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS},
267 {&CertVerifier::Config::enable_sha1_local_anchors,
268 CertVerifyProc::VERIFY_ENABLE_SHA1_LOCAL_ANCHORS},
269 {&CertVerifier::Config::disable_symantec_enforcement,
270 CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT},
271 };
272 for (const auto& test_config : kTestConfig) {
273 CertVerifier::Config config;
274 config.*test_config.config_ptr = true;
275
276 verifier_->SetConfig(config);
277
278 EXPECT_CALL(*mock_verify_proc_,
279 VerifyInternal(_, _, _, _, test_config.expected_flag, _, _))
280 .WillRepeatedly(
281 DoAll(SetCertVerifyRevokedResult(), Return(ERR_CERT_REVOKED)));
282
283 CertVerifyResult verify_result;
284 TestCompletionCallback callback;
285 std::unique_ptr<CertVerifier::Request> request;
286 int error = verifier_->Verify(
287 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
288 /*ocsp_response=*/std::string(),
289 /*sct_list=*/std::string()),
290 &verify_result, callback.callback(), &request, NetLogWithSource());
291 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
292 EXPECT_TRUE(request);
293 error = callback.WaitForResult();
294 EXPECT_TRUE(IsCertificateError(error));
295 EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
296
297 testing::Mock::VerifyAndClearExpectations(mock_verify_proc_.get());
298 }
299 }
300
301 // Tests propagation of CertVerifier flags into CertVerifyProc flags
TEST_F(MultiThreadedCertVerifierTest,ConvertsFlagsToFlags)302 TEST_F(MultiThreadedCertVerifierTest, ConvertsFlagsToFlags) {
303 scoped_refptr<X509Certificate> test_cert(
304 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
305 ASSERT_TRUE(test_cert);
306
307 EXPECT_CALL(
308 *mock_verify_proc_,
309 VerifyInternal(_, _, _, _, CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES,
310 _, _))
311 .WillRepeatedly(
312 DoAll(SetCertVerifyRevokedResult(), Return(ERR_CERT_REVOKED)));
313
314 CertVerifyResult verify_result;
315 TestCompletionCallback callback;
316 std::unique_ptr<CertVerifier::Request> request;
317 int error = verifier_->Verify(
318 CertVerifier::RequestParams(test_cert, "www.example.com",
319 CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES,
320 /*ocsp_response=*/std::string(),
321 /*sct_list=*/std::string()),
322 &verify_result, callback.callback(), &request, NetLogWithSource());
323 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
324 EXPECT_TRUE(request);
325 error = callback.WaitForResult();
326 EXPECT_TRUE(IsCertificateError(error));
327 EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
328
329 testing::Mock::VerifyAndClearExpectations(mock_verify_proc_.get());
330 }
331
332 // Tests swapping in new Chrome Root Store Data.
TEST_F(MultiThreadedCertVerifierTest,VerifyProcChangeChromeRootStore)333 TEST_F(MultiThreadedCertVerifierTest, VerifyProcChangeChromeRootStore) {
334 CertVerifierObserverCounter observer_counter(verifier_.get());
335
336 base::FilePath certs_dir = GetTestCertsDirectory();
337 scoped_refptr<X509Certificate> test_cert(
338 ImportCertFromFile(certs_dir, "ok_cert.pem"));
339 ASSERT_TRUE(test_cert);
340
341 EXPECT_EQ(observer_counter.change_count(), 0u);
342
343 EXPECT_CALL(*mock_new_verify_proc_, VerifyInternal(_, _, _, _, _, _, _))
344 .WillRepeatedly(
345 DoAll(SetCertVerifyRevokedResult(), Return(ERR_CERT_REVOKED)));
346 verifier_->UpdateVerifyProcData(nullptr, {}, {});
347
348 EXPECT_EQ(observer_counter.change_count(), 1u);
349
350 CertVerifyResult verify_result;
351 TestCompletionCallback callback;
352 std::unique_ptr<CertVerifier::Request> request;
353 int error = verifier_->Verify(
354 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
355 /*ocsp_response=*/std::string(),
356 /*sct_list=*/std::string()),
357 &verify_result, callback.callback(), &request, NetLogWithSource());
358 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
359 EXPECT_TRUE(request);
360 error = callback.WaitForResult();
361 EXPECT_TRUE(IsCertificateError(error));
362 EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
363
364 testing::Mock::VerifyAndClearExpectations(mock_verify_proc_.get());
365 testing::Mock::VerifyAndClearExpectations(mock_new_verify_proc_.get());
366 }
367
368 // Tests swapping out a new proc while a request is pending still uses
369 // the old proc for the old request.
TEST_F(MultiThreadedCertVerifierTest,VerifyProcChangeRequest)370 TEST_F(MultiThreadedCertVerifierTest, VerifyProcChangeRequest) {
371 base::FilePath certs_dir = GetTestCertsDirectory();
372 scoped_refptr<X509Certificate> test_cert(
373 ImportCertFromFile(certs_dir, "ok_cert.pem"));
374 ASSERT_TRUE(test_cert);
375
376 CertVerifyResult verify_result;
377 TestCompletionCallback callback;
378 std::unique_ptr<CertVerifier::Request> request;
379 int error = verifier_->Verify(
380 CertVerifier::RequestParams(test_cert, "www.example.com", 0,
381 /*ocsp_response=*/std::string(),
382 /*sct_list=*/std::string()),
383 &verify_result, callback.callback(), &request, NetLogWithSource());
384 ASSERT_THAT(error, IsError(ERR_IO_PENDING));
385 EXPECT_TRUE(request);
386 verifier_->UpdateVerifyProcData(nullptr, {}, {});
387 error = callback.WaitForResult();
388 EXPECT_TRUE(IsCertificateError(error));
389 EXPECT_THAT(error, IsError(ERR_CERT_COMMON_NAME_INVALID));
390
391 testing::Mock::VerifyAndClearExpectations(mock_verify_proc_.get());
392 testing::Mock::VerifyAndClearExpectations(mock_new_verify_proc_.get());
393 }
394
395 } // namespace net
396