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