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