Home | History | Annotate | Download | only in ssl
      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/ssl/client_cert_store_impl.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 
     10 #define SECURITY_WIN32  // Needs to be defined before including security.h
     11 #include <windows.h>
     12 #include <wincrypt.h>
     13 #include <security.h>
     14 
     15 #include "base/logging.h"
     16 #include "crypto/scoped_capi_types.h"
     17 #include "net/cert/x509_util.h"
     18 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 // Callback required by Windows API function CertFindChainInStore(). In addition
     24 // to filtering by extended/enhanced key usage, we do not show expired
     25 // certificates and require digital signature usage in the key usage extension.
     26 //
     27 // This matches our behavior on Mac OS X and that of NSS. It also matches the
     28 // default behavior of IE8. See http://support.microsoft.com/kb/890326 and
     29 // http://blogs.msdn.com/b/askie/archive/2009/06/09/my-expired-client-certifica
     30 //     tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx
     31 static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context,
     32                                           void* find_arg) {
     33   // Verify the certificate key usage is appropriate or not specified.
     34   BYTE key_usage;
     35   if (CertGetIntendedKeyUsage(X509_ASN_ENCODING, cert_context->pCertInfo,
     36                               &key_usage, 1)) {
     37     if (!(key_usage & CERT_DIGITAL_SIGNATURE_KEY_USAGE))
     38       return FALSE;
     39   } else {
     40     DWORD err = GetLastError();
     41     // If |err| is non-zero, it's an actual error. Otherwise the extension
     42     // just isn't present, and we treat it as if everything was allowed.
     43     if (err) {
     44       DLOG(ERROR) << "CertGetIntendedKeyUsage failed: " << err;
     45       return FALSE;
     46     }
     47   }
     48 
     49   // Verify the current time is within the certificate's validity period.
     50   if (CertVerifyTimeValidity(NULL, cert_context->pCertInfo) != 0)
     51     return FALSE;
     52 
     53   // Verify private key metadata is associated with this certificate.
     54   // TODO(ppi): Is this really needed? Isn't it equivalent to leaving
     55   // CERT_CHAIN_FIND_BY_ISSUER_NO_KEY_FLAG not set in |find_flags| argument of
     56   // CertFindChainInStore()?
     57   DWORD size = 0;
     58   if (!CertGetCertificateContextProperty(
     59           cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) {
     60     return FALSE;
     61   }
     62 
     63   return TRUE;
     64 }
     65 
     66 bool GetClientCertsImpl(HCERTSTORE cert_store,
     67                         const SSLCertRequestInfo& request,
     68                         CertificateList* selected_certs) {
     69   selected_certs->clear();
     70 
     71   const size_t auth_count = request.cert_authorities.size();
     72   std::vector<CERT_NAME_BLOB> issuers(auth_count);
     73   for (size_t i = 0; i < auth_count; ++i) {
     74     issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size());
     75     issuers[i].pbData = reinterpret_cast<BYTE*>(
     76         const_cast<char*>(request.cert_authorities[i].data()));
     77   }
     78 
     79   // Enumerate the client certificates.
     80   CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
     81   memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
     82   find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
     83   find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
     84   find_by_issuer_para.cIssuer = static_cast<DWORD>(auth_count);
     85   find_by_issuer_para.rgIssuer =
     86       reinterpret_cast<CERT_NAME_BLOB*>(issuers.data());
     87   find_by_issuer_para.pfnFindCallback = ClientCertFindCallback;
     88 
     89   PCCERT_CHAIN_CONTEXT chain_context = NULL;
     90   DWORD find_flags = CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG |
     91                      CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG;
     92   for (;;) {
     93     // Find a certificate chain.
     94     chain_context = CertFindChainInStore(cert_store,
     95                                          X509_ASN_ENCODING,
     96                                          find_flags,
     97                                          CERT_CHAIN_FIND_BY_ISSUER,
     98                                          &find_by_issuer_para,
     99                                          chain_context);
    100     if (!chain_context) {
    101       if (GetLastError() != CRYPT_E_NOT_FOUND)
    102         DPLOG(ERROR) << "CertFindChainInStore failed: ";
    103       break;
    104     }
    105 
    106     // Get the leaf certificate.
    107     PCCERT_CONTEXT cert_context =
    108         chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
    109     // Copy the certificate, so that it is valid after |cert_store| is closed.
    110     PCCERT_CONTEXT cert_context2 = NULL;
    111     BOOL ok = CertAddCertificateContextToStore(NULL, cert_context,
    112                                                CERT_STORE_ADD_USE_EXISTING,
    113                                                &cert_context2);
    114     if (!ok) {
    115       NOTREACHED();
    116       continue;
    117     }
    118 
    119     // Grab the intermediates, if any.
    120     X509Certificate::OSCertHandles intermediates;
    121     for (DWORD i = 1; i < chain_context->rgpChain[0]->cElement; ++i) {
    122       PCCERT_CONTEXT chain_intermediate =
    123           chain_context->rgpChain[0]->rgpElement[i]->pCertContext;
    124       PCCERT_CONTEXT copied_intermediate = NULL;
    125       ok = CertAddCertificateContextToStore(NULL, chain_intermediate,
    126                                             CERT_STORE_ADD_USE_EXISTING,
    127                                             &copied_intermediate);
    128       if (ok)
    129         intermediates.push_back(copied_intermediate);
    130     }
    131     scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
    132         cert_context2, intermediates);
    133     selected_certs->push_back(cert);
    134     CertFreeCertificateContext(cert_context2);
    135     for (size_t i = 0; i < intermediates.size(); ++i)
    136       CertFreeCertificateContext(intermediates[i]);
    137   }
    138 
    139   std::sort(selected_certs->begin(), selected_certs->end(),
    140             x509_util::ClientCertSorter());
    141   return true;
    142 }
    143 
    144 }  // namespace
    145 
    146 bool ClientCertStoreImpl::GetClientCerts(const SSLCertRequestInfo& request,
    147                                          CertificateList* selected_certs) {
    148   // Client certificates of the user are in the "MY" system certificate store.
    149   HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
    150   if (!my_cert_store) {
    151     PLOG(ERROR) << "Could not open the \"MY\" system certificate store: ";
    152     return false;
    153   }
    154 
    155   bool rv = GetClientCertsImpl(my_cert_store, request, selected_certs);
    156   if (!CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG)) {
    157     PLOG(ERROR) << "Could not close the \"MY\" system certificate store: ";
    158     return false;
    159   }
    160   return rv;
    161 }
    162 
    163 bool ClientCertStoreImpl::SelectClientCertsForTesting(
    164     const CertificateList& input_certs,
    165     const SSLCertRequestInfo& request,
    166     CertificateList* selected_certs) {
    167   typedef crypto::ScopedCAPIHandle<
    168       HCERTSTORE,
    169       crypto::CAPIDestroyerWithFlags<HCERTSTORE,
    170                                      CertCloseStore, 0> > ScopedHCERTSTORE;
    171 
    172   ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0,
    173                                             NULL));
    174   if (!test_store)
    175     return false;
    176 
    177   // Add available certificates to the test store.
    178   for (size_t i = 0; i < input_certs.size(); ++i) {
    179     // Add the certificate to the test store.
    180     PCCERT_CONTEXT cert = NULL;
    181     if (!CertAddCertificateContextToStore(test_store,
    182                                           input_certs[i]->os_cert_handle(),
    183                                           CERT_STORE_ADD_NEW, &cert)) {
    184       return false;
    185     }
    186     // Add dummy private key data to the certificate - otherwise the certificate
    187     // would be discarded by the filtering routines.
    188     CRYPT_KEY_PROV_INFO private_key_data;
    189     memset(&private_key_data, 0, sizeof(private_key_data));
    190     if (!CertSetCertificateContextProperty(cert,
    191                                            CERT_KEY_PROV_INFO_PROP_ID,
    192                                            0, &private_key_data)) {
    193       return false;
    194     }
    195     // Decrement the reference count of the certificate (since we requested a
    196     // copy).
    197     if (!CertFreeCertificateContext(cert))
    198       return false;
    199   }
    200 
    201   bool rv = GetClientCertsImpl(test_store.get(), request, selected_certs);
    202   return rv;
    203 }
    204 
    205 }  // namespace net
    206