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