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 #if !defined(LEAK_SANITIZER) 283 #define MAYBE_CancelRequestThenQuit CancelRequestThenQuit 284 #else 285 // See PR303886. LeakSanitizer flags a leak here. 286 #define MAYBE_CancelRequestThenQuit DISABLED_CancelRequestThenQuit 287 #endif 288 TEST_F(MultiThreadedCertVerifierTest, MAYBE_CancelRequestThenQuit) { 289 base::FilePath certs_dir = GetTestCertsDirectory(); 290 scoped_refptr<X509Certificate> test_cert( 291 ImportCertFromFile(certs_dir, "ok_cert.pem")); 292 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); 293 294 int error; 295 CertVerifyResult verify_result; 296 TestCompletionCallback callback; 297 CertVerifier::RequestHandle request_handle; 298 299 error = verifier_.Verify(test_cert.get(), 300 "www.example.com", 301 0, 302 NULL, 303 &verify_result, 304 callback.callback(), 305 &request_handle, 306 BoundNetLog()); 307 ASSERT_EQ(ERR_IO_PENDING, error); 308 EXPECT_TRUE(request_handle); 309 verifier_.CancelRequest(request_handle); 310 // Destroy |verifier| by going out of scope. 311 } 312 313 TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) { 314 SHA1HashValue a_key; 315 memset(a_key.data, 'a', sizeof(a_key.data)); 316 317 SHA1HashValue z_key; 318 memset(z_key.data, 'z', sizeof(z_key.data)); 319 320 const CertificateList empty_list; 321 CertificateList test_list; 322 test_list.push_back( 323 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); 324 325 struct { 326 // Keys to test 327 MultiThreadedCertVerifier::RequestParams key1; 328 MultiThreadedCertVerifier::RequestParams key2; 329 330 // Expectation: 331 // -1 means key1 is less than key2 332 // 0 means key1 equals key2 333 // 1 means key1 is greater than key2 334 int expected_result; 335 } tests[] = { 336 { // Test for basic equivalence. 337 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 338 0, test_list), 339 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 340 0, test_list), 341 0, 342 }, 343 { // Test that different certificates but with the same CA and for 344 // the same host are different validation keys. 345 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 346 0, test_list), 347 MultiThreadedCertVerifier::RequestParams(z_key, a_key, "www.example.test", 348 0, test_list), 349 -1, 350 }, 351 { // Test that the same EE certificate for the same host, but with 352 // different chains are different validation keys. 353 MultiThreadedCertVerifier::RequestParams(a_key, z_key, "www.example.test", 354 0, test_list), 355 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 356 0, test_list), 357 1, 358 }, 359 { // The same certificate, with the same chain, but for different 360 // hosts are different validation keys. 361 MultiThreadedCertVerifier::RequestParams(a_key, a_key, 362 "www1.example.test", 0, 363 test_list), 364 MultiThreadedCertVerifier::RequestParams(a_key, a_key, 365 "www2.example.test", 0, 366 test_list), 367 -1, 368 }, 369 { // The same certificate, chain, and host, but with different flags 370 // are different validation keys. 371 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 372 CertVerifier::VERIFY_EV_CERT, 373 test_list), 374 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 375 0, test_list), 376 1, 377 }, 378 { // Different additional_trust_anchors. 379 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 380 0, empty_list), 381 MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test", 382 0, test_list), 383 -1, 384 }, 385 }; 386 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 387 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i)); 388 389 const MultiThreadedCertVerifier::RequestParams& key1 = tests[i].key1; 390 const MultiThreadedCertVerifier::RequestParams& key2 = tests[i].key2; 391 392 switch (tests[i].expected_result) { 393 case -1: 394 EXPECT_TRUE(key1 < key2); 395 EXPECT_FALSE(key2 < key1); 396 break; 397 case 0: 398 EXPECT_FALSE(key1 < key2); 399 EXPECT_FALSE(key2 < key1); 400 break; 401 case 1: 402 EXPECT_FALSE(key1 < key2); 403 EXPECT_TRUE(key2 < key1); 404 break; 405 default: 406 FAIL() << "Invalid expectation. Can be only -1, 0, 1"; 407 } 408 } 409 } 410 411 TEST_F(MultiThreadedCertVerifierTest, CertTrustAnchorProvider) { 412 MockCertTrustAnchorProvider trust_provider; 413 verifier_.SetCertTrustAnchorProvider(&trust_provider); 414 415 scoped_refptr<X509Certificate> test_cert( 416 ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); 417 ASSERT_TRUE(test_cert.get()); 418 419 const CertificateList empty_cert_list; 420 CertificateList cert_list; 421 cert_list.push_back(test_cert); 422 423 // Check that Verify() asks the |trust_provider| for the current list of 424 // additional trust anchors. 425 int error; 426 CertVerifyResult verify_result; 427 TestCompletionCallback callback; 428 CertVerifier::RequestHandle request_handle; 429 EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) 430 .WillOnce(ReturnRef(empty_cert_list)); 431 error = verifier_.Verify(test_cert.get(), 432 "www.example.com", 433 0, 434 NULL, 435 &verify_result, 436 callback.callback(), 437 &request_handle, 438 BoundNetLog()); 439 Mock::VerifyAndClearExpectations(&trust_provider); 440 ASSERT_EQ(ERR_IO_PENDING, error); 441 EXPECT_TRUE(request_handle); 442 error = callback.WaitForResult(); 443 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); 444 ASSERT_EQ(1u, verifier_.requests()); 445 ASSERT_EQ(0u, verifier_.cache_hits()); 446 447 // The next Verify() uses the cached result. 448 EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) 449 .WillOnce(ReturnRef(empty_cert_list)); 450 error = verifier_.Verify(test_cert.get(), 451 "www.example.com", 452 0, 453 NULL, 454 &verify_result, 455 callback.callback(), 456 &request_handle, 457 BoundNetLog()); 458 Mock::VerifyAndClearExpectations(&trust_provider); 459 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); 460 EXPECT_FALSE(request_handle); 461 ASSERT_EQ(2u, verifier_.requests()); 462 ASSERT_EQ(1u, verifier_.cache_hits()); 463 464 // Another Verify() for the same certificate but with a different list of 465 // trust anchors will not reuse the cache. 466 EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors()) 467 .WillOnce(ReturnRef(cert_list)); 468 error = verifier_.Verify(test_cert.get(), 469 "www.example.com", 470 0, 471 NULL, 472 &verify_result, 473 callback.callback(), 474 &request_handle, 475 BoundNetLog()); 476 Mock::VerifyAndClearExpectations(&trust_provider); 477 ASSERT_EQ(ERR_IO_PENDING, error); 478 EXPECT_TRUE(request_handle); 479 error = callback.WaitForResult(); 480 EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error); 481 ASSERT_EQ(3u, verifier_.requests()); 482 ASSERT_EQ(1u, verifier_.cache_hits()); 483 } 484 485 } // namespace net 486