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 "services/network/public/cpp/cert_verifier/mojo_cert_verifier.h"
6 
7 #include <map>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/run_loop.h"
12 #include "base/test/bind.h"
13 #include "base/test/task_environment.h"
14 #include "mojo/public/cpp/bindings/pending_remote.h"
15 #include "net/base/test_completion_callback.h"
16 #include "net/cert/cert_status_flags.h"
17 #include "net/cert/cert_verifier.h"
18 #include "net/cert/cert_verify_result.h"
19 #include "net/log/net_log_with_source.h"
20 #include "net/test/cert_test_util.h"
21 #include "net/test/test_data_directory.h"
22 #include "services/network/public/mojom/cert_verifier_service.mojom.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "testing/platform_test.h"
25 
26 namespace cert_verifier {
27 namespace {
28 const int kExpectedNetError = net::ERR_CERT_INVALID;
29 const unsigned int kExpectedCertStatus = net::CERT_STATUS_INVALID;
30 
GetTestCert()31 scoped_refptr<net::X509Certificate> GetTestCert() {
32   return net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
33 }
34 
GetDummyParams()35 net::CertVerifier::RequestParams GetDummyParams() {
36   return net::CertVerifier::RequestParams(GetTestCert(), "example.com", 0,
37                                           /*ocsp_response=*/std::string(),
38                                           /*sct_list=*/std::string());
39 }
40 
41 mojo::PendingRemote<network::mojom::URLLoaderFactory>
CreateUnconnectedURLLoaderFactory()42 CreateUnconnectedURLLoaderFactory() {
43   mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
44   // Bind the factory, but don't bother connecting it.
45   ignore_result(url_loader_factory.InitWithNewPipeAndPassReceiver());
46   return url_loader_factory;
47 }
48 
49 class MojoCertVerifierTest : public PlatformTest {
50  public:
MojoCertVerifierTest()51   MojoCertVerifierTest()
52       : dummy_cv_service_(this),
53         cv_service_receiver_(&dummy_cv_service_),
54         mojo_cert_verifier_(
55             cv_service_receiver_.BindNewPipeAndPassRemote(),
56             CreateUnconnectedURLLoaderFactory(),
57             base::BindRepeating(&MojoCertVerifierTest::ReconnectCb,
58                                 base::Unretained(this))) {
59     // Any Mojo requests in the MojoCertVerifier constructor should run here.
60     mojo_cert_verifier_.FlushForTesting();
61   }
62 
63   class DummyCVService final : public mojom::CertVerifierService {
64    public:
DummyCVService(MojoCertVerifierTest * test)65     explicit DummyCVService(MojoCertVerifierTest* test) : test_(test) {}
Verify(const net::CertVerifier::RequestParams & params,mojo::PendingRemote<mojom::CertVerifierRequest> cert_verifier_request)66     void Verify(const net::CertVerifier::RequestParams& params,
67                 mojo::PendingRemote<mojom::CertVerifierRequest>
68                     cert_verifier_request) override {
69       ASSERT_FALSE(test_->received_requests_.count(params));
70       test_->received_requests_[params].Bind(std::move(cert_verifier_request));
71     }
72 
SetConfig(const net::CertVerifier::Config & config)73     void SetConfig(const net::CertVerifier::Config& config) override {
74       config_ = config;
75     }
76 
EnableNetworkAccess(mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory,mojo::PendingRemote<mojom::URLLoaderFactoryConnector> reconnector)77     void EnableNetworkAccess(
78         mojo::PendingRemote<network::mojom::URLLoaderFactory>
79             url_loader_factory,
80         mojo::PendingRemote<mojom::URLLoaderFactoryConnector> reconnector)
81         override {
82       reconnector_.Bind(std::move(reconnector));
83     }
84 
config() const85     const net::CertVerifier::Config* config() const { return &config_; }
reconnector()86     mojom::URLLoaderFactoryConnector* reconnector() {
87       return reconnector_.get();
88     }
89 
90    private:
91     MojoCertVerifierTest* test_;
92 
93     net::CertVerifier::Config config_;
94     mojo::Remote<mojom::URLLoaderFactoryConnector> reconnector_;
95   };
96 
task_environment()97   base::test::TaskEnvironment* task_environment() { return &task_environment_; }
98 
mojo_cert_verifier()99   MojoCertVerifier* mojo_cert_verifier() { return &mojo_cert_verifier_; }
100 
net_log_with_source()101   net::NetLogWithSource* net_log_with_source() {
102     return &empty_net_log_with_source_;
103   }
104 
dummy_cv_service()105   DummyCVService* dummy_cv_service() { return &dummy_cv_service_; }
106 
RespondToRequest(const net::CertVerifier::RequestParams & params)107   void RespondToRequest(const net::CertVerifier::RequestParams& params) {
108     ASSERT_TRUE(received_requests_.count(params));
109     net::CertVerifyResult result;
110     result.cert_status = kExpectedCertStatus;
111     received_requests_[params]->Complete(result, kExpectedNetError);
112   }
113 
DidRequestDisconnect(const net::CertVerifier::RequestParams & params)114   bool DidRequestDisconnect(const net::CertVerifier::RequestParams& params) {
115     if (!received_requests_.count(params)) {
116       ADD_FAILURE();
117       return false;
118     }
119     return !received_requests_[params].is_connected();
120   }
121 
DisconnectRequest(const net::CertVerifier::RequestParams & params)122   void DisconnectRequest(const net::CertVerifier::RequestParams& params) {
123     ASSERT_TRUE(received_requests_.count(params));
124     received_requests_[params].reset();
125   }
126 
SimulateCVServiceDisconnect()127   void SimulateCVServiceDisconnect() { cv_service_receiver_.reset(); }
128 
ReconnectCb(mojo::PendingReceiver<network::mojom::URLLoaderFactory> mojo_cert_verifier)129   void ReconnectCb(mojo::PendingReceiver<network::mojom::URLLoaderFactory>
130                        mojo_cert_verifier) {
131     if (reconnect_cb_)
132       reconnect_cb_.Run(std::move(mojo_cert_verifier));
133   }
134 
SetReconnectCb(MojoCertVerifier::ReconnectURLLoaderFactory cb)135   void SetReconnectCb(MojoCertVerifier::ReconnectURLLoaderFactory cb) {
136     reconnect_cb_ = std::move(cb);
137   }
138 
139  private:
140   std::map<net::CertVerifier::RequestParams,
141            mojo::Remote<mojom::CertVerifierRequest>>
142       received_requests_;
143 
144   base::test::TaskEnvironment task_environment_;
145 
146   MojoCertVerifierTest::DummyCVService dummy_cv_service_;
147   mojo::Receiver<mojom::CertVerifierService> cv_service_receiver_;
148 
149   MojoCertVerifier mojo_cert_verifier_;
150 
151   MojoCertVerifier::ReconnectURLLoaderFactory reconnect_cb_;
152 
153   net::NetLogWithSource empty_net_log_with_source_;
154 };
155 }  // namespace
156 
TEST_F(MojoCertVerifierTest,BasicVerify)157 TEST_F(MojoCertVerifierTest, BasicVerify) {
158   net::CertVerifier::RequestParams dummy_params = GetDummyParams();
159 
160   auto cert_verify_result = std::make_unique<net::CertVerifyResult>();
161   net::TestCompletionCallback test_cb;
162   std::unique_ptr<net::CertVerifier::Request> request;
163 
164   int net_error = mojo_cert_verifier()->Verify(
165       dummy_params, cert_verify_result.get(), test_cb.callback(), &request,
166       *net_log_with_source());
167   ASSERT_EQ(net_error, net::ERR_IO_PENDING);
168 
169   // Handle Mojo request
170   task_environment()->RunUntilIdle();
171 
172   RespondToRequest(dummy_params);
173 
174   net_error = test_cb.WaitForResult();
175   EXPECT_EQ(net_error, kExpectedNetError);
176   EXPECT_EQ(cert_verify_result->cert_status, kExpectedCertStatus);
177 }
178 
179 // Same as BasicVerify, except we disconnect the Remote<CertVerifierRequest>
180 // right after responding, to test that we don't accidentally handle both the
181 // normal response and the disconnection.
TEST_F(MojoCertVerifierTest,BasicVerifyAndRequestDisconnection)182 TEST_F(MojoCertVerifierTest, BasicVerifyAndRequestDisconnection) {
183   net::CertVerifier::RequestParams dummy_params = GetDummyParams();
184 
185   auto cert_verify_result = std::make_unique<net::CertVerifyResult>();
186   net::TestCompletionCallback test_cb;
187   std::unique_ptr<net::CertVerifier::Request> request;
188 
189   int net_error = mojo_cert_verifier()->Verify(
190       dummy_params, cert_verify_result.get(), test_cb.callback(), &request,
191       *net_log_with_source());
192   ASSERT_EQ(net_error, net::ERR_IO_PENDING);
193 
194   // Handle Mojo request
195   task_environment()->RunUntilIdle();
196 
197   RespondToRequest(dummy_params);
198   // Disconnect the request right after responding to it
199   DisconnectRequest(dummy_params);
200 
201   net_error = test_cb.WaitForResult();
202   EXPECT_EQ(net_error, kExpectedNetError);
203   EXPECT_EQ(cert_verify_result->cert_status, kExpectedCertStatus);
204 }
205 
TEST_F(MojoCertVerifierTest,CVRequestDeletionCausesDisconnect)206 TEST_F(MojoCertVerifierTest, CVRequestDeletionCausesDisconnect) {
207   net::CertVerifier::RequestParams dummy_params = GetDummyParams();
208 
209   auto cert_verify_result = std::make_unique<net::CertVerifyResult>();
210   auto verify_cb = base::BindRepeating([](int net_error) {
211     // Should never get called as the request will be cancelled on the
212     // MojoCertVerifier side.
213     GTEST_FAIL();
214   });
215   std::unique_ptr<net::CertVerifier::Request> request;
216 
217   int net_error = mojo_cert_verifier()->Verify(
218       dummy_params, cert_verify_result.get(), std::move(verify_cb), &request,
219       *net_log_with_source());
220   ASSERT_EQ(net_error, net::ERR_IO_PENDING);
221 
222   // Reset our cert verifier request to cause a disconnection.
223   request.reset();
224 
225   task_environment()->RunUntilIdle();
226 
227   EXPECT_TRUE(DidRequestDisconnect(dummy_params));
228 }
229 
TEST_F(MojoCertVerifierTest,HandlesMojomCVRequestDisconnectionAsCancellation)230 TEST_F(MojoCertVerifierTest, HandlesMojomCVRequestDisconnectionAsCancellation) {
231   net::CertVerifier::RequestParams dummy_params = GetDummyParams();
232 
233   auto cert_verify_result = std::make_unique<net::CertVerifyResult>();
234   net::TestCompletionCallback test_cb;
235   std::unique_ptr<net::CertVerifier::Request> request;
236 
237   int net_error = mojo_cert_verifier()->Verify(
238       dummy_params, cert_verify_result.get(), test_cb.callback(), &request,
239       *net_log_with_source());
240   ASSERT_EQ(net_error, net::ERR_IO_PENDING);
241 
242   // Handle Mojo request
243   task_environment()->RunUntilIdle();
244 
245   DisconnectRequest(dummy_params);
246 
247   net_error = test_cb.WaitForResult();
248   // Should abort if cancelled on the CertVerifierService side.
249   EXPECT_EQ(net_error, net::ERR_ABORTED);
250   EXPECT_EQ(cert_verify_result->cert_status, kExpectedCertStatus);
251 }
252 
TEST_F(MojoCertVerifierTest,IgnoresCVServiceDisconnection)253 TEST_F(MojoCertVerifierTest, IgnoresCVServiceDisconnection) {
254   net::CertVerifier::RequestParams dummy_params = GetDummyParams();
255 
256   auto cert_verify_result = std::make_unique<net::CertVerifyResult>();
257   net::TestCompletionCallback test_cb;
258   std::unique_ptr<net::CertVerifier::Request> request;
259 
260   int net_error = mojo_cert_verifier()->Verify(
261       dummy_params, cert_verify_result.get(), test_cb.callback(), &request,
262       *net_log_with_source());
263   ASSERT_EQ(net_error, net::ERR_IO_PENDING);
264 
265   // Handle Mojo request
266   task_environment()->RunUntilIdle();
267 
268   SimulateCVServiceDisconnect();
269   RespondToRequest(dummy_params);
270 
271   net_error = test_cb.WaitForResult();
272   EXPECT_EQ(net_error, kExpectedNetError);
273   EXPECT_EQ(cert_verify_result->cert_status, kExpectedCertStatus);
274 }
275 
TEST_F(MojoCertVerifierTest,SendsConfig)276 TEST_F(MojoCertVerifierTest, SendsConfig) {
277   ASSERT_FALSE(dummy_cv_service()->config()->disable_symantec_enforcement);
278 
279   net::CertVerifier::Config config;
280   config.disable_symantec_enforcement = true;
281 
282   mojo_cert_verifier()->SetConfig(config);
283   task_environment()->RunUntilIdle();
284 
285   ASSERT_TRUE(dummy_cv_service()->config()->disable_symantec_enforcement);
286 }
287 
TEST_F(MojoCertVerifierTest,ReconnectorCallsCb)288 TEST_F(MojoCertVerifierTest, ReconnectorCallsCb) {
289   base::RunLoop run_loop;
290   SetReconnectCb(base::BindLambdaForTesting(
291       [&](mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
292         run_loop.Quit();
293       }));
294 
295   mojo::PendingRemote<network::mojom::URLLoaderFactory>
296       dummy_url_loader_factory_remote;
297   // Simulate a remote CertVerifierService trying to reconnect after
298   // disconnection. This should call the callback given to the MojoCertVerifier
299   // on construction.
300   dummy_cv_service()->reconnector()->CreateURLLoaderFactory(
301       dummy_url_loader_factory_remote.InitWithNewPipeAndPassReceiver());
302 
303   run_loop.Run();
304 }
305 
306 }  // namespace cert_verifier
307