1 // Copyright (c) 2012 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 "net/cert/multi_threaded_cert_verifier.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_path.h" 9 #include "base/format_macros.h" 10 #include "base/strings/stringprintf.h" 11 #include "net/base/net_errors.h" 12 #include "net/base/net_log.h" 13 #include "net/base/test_completion_callback.h" 14 #include "net/base/test_data_directory.h" 15 #include "net/cert/cert_trust_anchor_provider.h" 16 #include "net/cert/cert_verify_proc.h" 17 #include "net/cert/cert_verify_result.h" 18 #include "net/cert/x509_certificate.h" 19 #include "net/test/cert_test_util.h" 20 #include "testing/gmock/include/gmock/gmock.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 using testing::Mock; 24 using testing::ReturnRef; 25 26 namespace net { 27 28 namespace { 29 30 void FailTest(int /* result */) { 31 FAIL(); 32 } 33 34 class MockCertVerifyProc : public CertVerifyProc { 35 public: 36 MockCertVerifyProc() {} 37 38 private: 39 virtual ~MockCertVerifyProc() {} 40 41 // CertVerifyProc implementation 42 virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE { 43 return false; 44 } 45 46 virtual int VerifyInternal(X509Certificate* cert, 47 const std::string& hostname, 48 int flags, 49 CRLSet* crl_set, 50 const CertificateList& additional_trust_anchors, 51 CertVerifyResult* verify_result) OVERRIDE { 52 verify_result->Reset(); 53 verify_result->verified_cert = cert; 54 verify_result->cert_status = CERT_STATUS_COMMON_NAME_INVALID; 55 return ERR_CERT_COMMON_NAME_INVALID; 56 } 57 }; 58 59 class MockCertTrustAnchorProvider : public CertTrustAnchorProvider { 60 public: 61 MockCertTrustAnchorProvider() {} 62 virtual ~MockCertTrustAnchorProvider() {} 63 64 MOCK_METHOD0(GetAdditionalTrustAnchors, const CertificateList&()); 65 }; 66 67 } // namespace 68 69 class MultiThreadedCertVerifierTest : public ::testing::Test { 70 public: 71 MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {} 72 virtual ~MultiThreadedCertVerifierTest() {} 73 74 protected: 75 MultiThreadedCertVerifier verifier_; 76 }; 77 78 TEST_F(MultiThreadedCertVerifierTest, CacheHit) { 79 base::FilePath certs_dir = GetTestCertsDirectory(); 80 scoped_refptr<X509Certificate> test_cert( 81 ImportCertFromFile(certs_dir, "ok_cert.pem")); 82 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); 83 84 int error; 85 CertVerifyResult verify_result; 86 TestCompletionCallback callback; 87 CertVerifier::RequestHandle request_handle; 88 89 error = verifier_.Verify(test_cert.get(), 90 "www.example.com", 91 0, 92 NULL, 93 &verify_result, 94 callback.callback(), 95 &request_handle, 96 BoundNetLog()); 97 ASSERT_EQ(ERR_IO_PENDING, error); 98 EXPECT_TRUE(request_handle); 99 error = callback.WaitForResult(); 100 ASSERT_TRUE(IsCertificateError(error)); 101 ASSERT_EQ(1u, verifier_.requests()); 102 ASSERT_EQ(0u, verifier_.cache_hits()); 103 ASSERT_EQ(0u, verifier_.inflight_joins()); 104 ASSERT_EQ(1u, verifier_.GetCacheSize()); 105 106 error = verifier_.Verify(test_cert.get(), 107 "www.example.com", 108 0, 109 NULL, 110 &verify_result, 111 callback.callback(), 112 &request_handle, 113 BoundNetLog()); 114 // Synchronous completion. 115 ASSERT_NE(ERR_IO_PENDING, error); 116 ASSERT_TRUE(IsCertificateError(error)); 117 ASSERT_TRUE(request_handle == NULL); 118 ASSERT_EQ(2u, verifier_.requests()); 119 ASSERT_EQ(1u, verifier_.cache_hits()); 120 ASSERT_EQ(0u, verifier_.inflight_joins()); 121 ASSERT_EQ(1u, verifier_.GetCacheSize()); 122 } 123 124 // Tests the same server certificate with different intermediate CA 125 // certificates. These should be treated as different certificate chains even 126 // though the two X509Certificate objects contain the same server certificate. 127 TEST_F(MultiThreadedCertVerifierTest, DifferentCACerts) { 128 base::FilePath certs_dir = GetTestCertsDirectory(); 129 130 scoped_refptr<X509Certificate> server_cert = 131 ImportCertFromFile(certs_dir, "salesforce_com_test.pem"); 132 ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert); 133 134 scoped_refptr<X509Certificate> intermediate_cert1 = 135 ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem"); 136 ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert1); 137 138 scoped_refptr<X509Certificate> intermediate_cert2 = 139 ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem"); 140 ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert2); 141 142 X509Certificate::OSCertHandles intermediates; 143 intermediates.push_back(intermediate_cert1->os_cert_handle()); 144 scoped_refptr<X509Certificate> cert_chain1 = 145 X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), 146 intermediates); 147 148 intermediates.clear(); 149 intermediates.push_back(intermediate_cert2->os_cert_handle()); 150 scoped_refptr<X509Certificate> cert_chain2 = 151 X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), 152 intermediates); 153 154 int error; 155 CertVerifyResult verify_result; 156 TestCompletionCallback callback; 157 CertVerifier::RequestHandle request_handle; 158 159 error = verifier_.Verify(cert_chain1.get(), 160 "www.example.com", 161 0, 162 NULL, 163 &verify_result, 164 callback.callback(), 165 &request_handle, 166 BoundNetLog()); 167 ASSERT_EQ(ERR_IO_PENDING, error); 168 EXPECT_TRUE(request_handle); 169 error = callback.WaitForResult(); 170 ASSERT_TRUE(IsCertificateError(error)); 171 ASSERT_EQ(1u, verifier_.requests()); 172 ASSERT_EQ(0u, verifier_.cache_hits()); 173 ASSERT_EQ(0u, verifier_.inflight_joins()); 174 ASSERT_EQ(1u, verifier_.GetCacheSize()); 175 176 error = verifier_.Verify(cert_chain2.get(), 177 "www.example.com", 178 0, 179 NULL, 180 &verify_result, 181 callback.callback(), 182 &request_handle, 183 BoundNetLog()); 184 ASSERT_EQ(ERR_IO_PENDING, error); 185 EXPECT_TRUE(request_handle); 186 error = callback.WaitForResult(); 187 ASSERT_TRUE(IsCertificateError(error)); 188 ASSERT_EQ(2u, verifier_.requests()); 189 ASSERT_EQ(0u, verifier_.cache_hits()); 190 ASSERT_EQ(0u, verifier_.inflight_joins()); 191 ASSERT_EQ(2u, verifier_.GetCacheSize()); 192 } 193 194 // Tests an inflight join. 195 TEST_F(MultiThreadedCertVerifierTest, InflightJoin) { 196 base::FilePath certs_dir = GetTestCertsDirectory(); 197 scoped_refptr<X509Certificate> test_cert( 198 ImportCertFromFile(certs_dir, "ok_cert.pem")); 199 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); 200 201 int error; 202 CertVerifyResult verify_result; 203 TestCompletionCallback callback; 204 CertVerifier::RequestHandle request_handle; 205 CertVerifyResult verify_result2; 206 TestCompletionCallback callback2; 207 CertVerifier::RequestHandle request_handle2; 208 209 error = verifier_.Verify(test_cert.get(), 210 "www.example.com", 211 0, 212 NULL, 213 &verify_result, 214 callback.callback(), 215 &request_handle, 216 BoundNetLog()); 217 ASSERT_EQ(ERR_IO_PENDING, error); 218 EXPECT_TRUE(request_handle); 219 error = verifier_.Verify(test_cert.get(), 220 "www.example.com", 221 0, 222 NULL, 223 &verify_result2, 224 callback2.callback(), 225 &request_handle2, 226 BoundNetLog()); 227 EXPECT_EQ(ERR_IO_PENDING, error); 228 EXPECT_TRUE(request_handle2 != NULL); 229 error = callback.WaitForResult(); 230 EXPECT_TRUE(IsCertificateError(error)); 231 error = callback2.WaitForResult(); 232 ASSERT_TRUE(IsCertificateError(error)); 233 ASSERT_EQ(2u, verifier_.requests()); 234 ASSERT_EQ(0u, verifier_.cache_hits()); 235 ASSERT_EQ(1u, verifier_.inflight_joins()); 236 } 237 238 // Tests that the callback of a canceled request is never made. 239 TEST_F(MultiThreadedCertVerifierTest, CancelRequest) { 240 base::FilePath certs_dir = GetTestCertsDirectory(); 241 scoped_refptr<X509Certificate> test_cert( 242 ImportCertFromFile(certs_dir, "ok_cert.pem")); 243 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); 244 245 int error; 246 CertVerifyResult verify_result; 247 CertVerifier::RequestHandle request_handle; 248 249 error = verifier_.Verify(test_cert.get(), 250 "www.example.com", 251 0, 252 NULL, 253 &verify_result, 254 base::Bind(&FailTest), 255 &request_handle, 256 BoundNetLog()); 257 ASSERT_EQ(ERR_IO_PENDING, error); 258 ASSERT_TRUE(request_handle != NULL); 259 verifier_.CancelRequest(request_handle); 260 261 // Issue a few more requests to the worker pool and wait for their 262 // completion, so that the task of the canceled request (which runs on a 263 // worker thread) is likely to complete by the end of this test. 264 TestCompletionCallback callback; 265 for (int i = 0; i < 5; ++i) { 266 error = verifier_.Verify(test_cert.get(), 267 "www2.example.com", 268 0, 269 NULL, 270 &verify_result, 271 callback.callback(), 272 &request_handle, 273 BoundNetLog()); 274 ASSERT_EQ(ERR_IO_PENDING, error); 275 EXPECT_TRUE(request_handle); 276 error = callback.WaitForResult(); 277 verifier_.ClearCache(); 278 } 279 } 280 281 // Tests that a canceled request is not leaked. 282 TEST_F(MultiThreadedCertVerifierTest, CancelRequestThenQuit) { 283 base::FilePath certs_dir = GetTestCertsDirectory(); 284 scoped_refptr<X509Certificate> test_cert( 285 ImportCertFromFile(certs_dir, "ok_cert.pem")); 286 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); 287 288 int error; 289 CertVerifyResult verify_result; 290 TestCompletionCallback callback; 291 CertVerifier::RequestHandle request_handle; 292 293 error = verifier_.Verify(test_cert.get(), 294 "www.example.com", 295 0, 296 NULL, 297 &verify_result, 298 callback.callback(), 299 &request_handle, 300 BoundNetLog()); 301 ASSERT_EQ(ERR_IO_PENDING, error); 302 EXPECT_TRUE(request_handle); 303 verifier_.CancelRequest(request_handle); 304 // Destroy |verifier| by going out of scope. 305 } 306 307 TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) { 308 SHA1HashValue a_key; 309 memset(a_key.data, 'a', sizeof(a_key.data)); 310 311 SHA1HashValue z_key; 312 memset(z_key.data, 'z', sizeof(z_key.data)); 313 314 const CertificateList empty_list; 315 CertificateList test_list; 316 test_list.push_back( 317 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); 318 319 struct { 320 // Keys to test 321 MultiThreadedCertVerifier::RequestParams key1; 322 MultiThreadedCertVerifier::RequestParams key2; 323 324 // Expectation: 325 // -1 means key1 is less than key2 326 // 0 means key1 equals key2 327 // 1 means key1 is greater than key2 328 int expected_result; 329 } tests[] = { 330 { // Test for basic equivalence. 331 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 332 0, test_list), 333 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 334 0, test_list), 335 0, 336 }, 337 { // Test that different certificates but with the same CA and for 338 // the same host are different validation keys. 339 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 340 0, test_list), 341 MultiThreadedCertVerifier::RequestParams(z_key, a_key, "www.example.test", 342 0, test_list), 343 -1, 344 }, 345 { // Test that the same EE certificate for the same host, but with 346 // different chains are different validation keys. 347 MultiThreadedCertVerifier::RequestParams(a_key, z_key, "www.example.test", 348 0, test_list), 349 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 350 0, test_list), 351 1, 352 }, 353 { // The same certificate, with the same chain, but for different 354 // hosts are different validation keys. 355 MultiThreadedCertVerifier::RequestParams(a_key, a_key, 356 "www1.example.test", 0, 357 test_list), 358 MultiThreadedCertVerifier::RequestParams(a_key, a_key, 359 "www2.example.test", 0, 360 test_list), 361 -1, 362 }, 363 { // The same certificate, chain, and host, but with different flags 364 // are different validation keys. 365 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 366 CertVerifier::VERIFY_EV_CERT, 367 test_list), 368 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 369 0, test_list), 370 1, 371 }, 372 { // Different additional_trust_anchors. 373 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 374 0, empty_list), 375 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 376 0, test_list), 377 -1, 378 }, 379 }; 380 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 381 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i)); 382 383 const MultiThreadedCertVerifier::RequestParams& key1 = tests[i].key1; 384 const MultiThreadedCertVerifier::RequestParams& key2 = tests[i].key2; 385 386 switch (tests[i].expected_result) { 387 case -1: 388 EXPECT_TRUE(key1 < key2); 389 EXPECT_FALSE(key2 < key1); 390 break; 391 case 0: 392 EXPECT_FALSE(key1 < key2); 393 EXPECT_FALSE(key2 < key1); 394 break; 395 case 1: 396 EXPECT_FALSE(key1 < key2); 397 EXPECT_TRUE(key2 < key1); 398 break; 399 default: 400 FAIL() << "Invalid expectation. Can be only -1, 0, 1"; 401 } 402 } 403 } 404 405 TEST_F(MultiThreadedCertVerifierTest, CertTrustAnchorProvider) { 406 MockCertTrustAnchorProvider trust_provider; 407 verifier_.SetCertTrustAnchorProvider(&trust_provider); 408 409 scoped_refptr<X509Certificate> test_cert( 410 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); 411 ASSERT_TRUE(test_cert.get()); 412 413 const CertificateList empty_cert_list; 414 CertificateList cert_list; 415 cert_list.push_back(test_cert); 416 417 // Check that Verify() asks the |trust_provider| for the current list of 418 // additional trust anchors. 419 int error; 420 CertVerifyResult verify_result; 421 TestCompletionCallback callback; 422 CertVerifier::RequestHandle request_handle; 423 EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) 424 .WillOnce(ReturnRef(empty_cert_list)); 425 error = verifier_.Verify(test_cert.get(), 426 "www.example.com", 427 0, 428 NULL, 429 &verify_result, 430 callback.callback(), 431 &request_handle, 432 BoundNetLog()); 433 Mock::VerifyAndClearExpectations(&trust_provider); 434 ASSERT_EQ(ERR_IO_PENDING, error); 435 EXPECT_TRUE(request_handle); 436 error = callback.WaitForResult(); 437 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); 438 ASSERT_EQ(1u, verifier_.requests()); 439 ASSERT_EQ(0u, verifier_.cache_hits()); 440 441 // The next Verify() uses the cached result. 442 EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) 443 .WillOnce(ReturnRef(empty_cert_list)); 444 error = verifier_.Verify(test_cert.get(), 445 "www.example.com", 446 0, 447 NULL, 448 &verify_result, 449 callback.callback(), 450 &request_handle, 451 BoundNetLog()); 452 Mock::VerifyAndClearExpectations(&trust_provider); 453 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); 454 EXPECT_FALSE(request_handle); 455 ASSERT_EQ(2u, verifier_.requests()); 456 ASSERT_EQ(1u, verifier_.cache_hits()); 457 458 // Another Verify() for the same certificate but with a different list of 459 // trust anchors will not reuse the cache. 460 EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) 461 .WillOnce(ReturnRef(cert_list)); 462 error = verifier_.Verify(test_cert.get(), 463 "www.example.com", 464 0, 465 NULL, 466 &verify_result, 467 callback.callback(), 468 &request_handle, 469 BoundNetLog()); 470 Mock::VerifyAndClearExpectations(&trust_provider); 471 ASSERT_EQ(ERR_IO_PENDING, error); 472 EXPECT_TRUE(request_handle); 473 error = callback.WaitForResult(); 474 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); 475 ASSERT_EQ(3u, verifier_.requests()); 476 ASSERT_EQ(1u, verifier_.cache_hits()); 477 } 478 479 } // namespace net 480