Home | History | Annotate | Download | only in cert
      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