| Index: net/base/cert_database_win.cc
|
| ===================================================================
|
| --- net/base/cert_database_win.cc (revision 49024)
|
| +++ net/base/cert_database_win.cc (working copy)
|
| @@ -9,7 +9,10 @@
|
| #pragma comment(lib, "crypt32.lib")
|
|
|
| #include "base/logging.h"
|
| +#include "base/scoped_ptr.h"
|
| #include "base/string_util.h"
|
| +#include "base/time.h"
|
| +#include "base/crypto/scoped_capi_types.h"
|
| #include "net/base/keygen_handler.h"
|
| #include "net/base/net_errors.h"
|
| #include "net/base/x509_certificate.h"
|
| @@ -18,60 +21,133 @@
|
|
|
| namespace {
|
|
|
| -// Returns an encoded version of SubjectPublicKeyInfo from |cert| that is
|
| -// compatible with KeygenHandler::Cache. If the cert cannot be converted, an
|
| -// empty string is returned.
|
| -std::string GetSubjectPublicKeyInfo(const X509Certificate* cert) {
|
| - DCHECK(cert);
|
| +typedef base::ScopedCAPIHandle<PCCERT_CHAIN_CONTEXT,
|
| + base::CAPIDestroyerVOID<PCCERT_CHAIN_CONTEXT,
|
| + CertFreeCertificateChain> >
|
| + ScopedPCCERT_CHAIN_CONTEXT;
|
| +typedef base::ScopedCAPIHandle<HCERTSTORE,
|
| + base::CAPIDestroyerWithFlags<HCERTSTORE, CertCloseStore, 0> >
|
| + ScopedHCERTSTORE;
|
|
|
| - std::string result;
|
| - if (!cert->os_cert_handle() || !cert->os_cert_handle()->pCertInfo)
|
| - return result;
|
| +// From an arbitrary, unordered list of certificate, attempt to import every
|
| +// certificate that:
|
| +// 1) Is a CA
|
| +// 2) Results in a valid certificate chain
|
| +void ImportValidIntermediates(
|
| + const X509Certificate::OSCertHandles& intermediates) {
|
|
|
| - BOOL ok;
|
| - DWORD size = 0;
|
| - PCERT_PUBLIC_KEY_INFO key_info =
|
| - &(cert->os_cert_handle()->pCertInfo->SubjectPublicKeyInfo);
|
| - ok = CryptEncodeObject(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, key_info,
|
| - NULL, &size);
|
| - if (!ok)
|
| - return result;
|
| + // First, create a local in memory store. The handles in intermediates may
|
| + // be from one or more stores, or not even be in a formal store (eg: when
|
| + // decoded from a byte sequence by X509Certificate), and thus may not be
|
| + // picked up by the default HCERTCHAINENGINE when it's attempting to build
|
| + ScopedHCERTSTORE temp_store(
|
| + CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL,
|
| + CERT_STORE_CREATE_NEW_FLAG, NULL));
|
| + if (temp_store == NULL)
|
| + return;
|
|
|
| - ok = CryptEncodeObject(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, key_info,
|
| - reinterpret_cast<BYTE*>(WriteInto(&result, size + 1)),
|
| - &size);
|
| - if (!ok) {
|
| - result.clear();
|
| - return result;
|
| + // Add links to all of the existing contexts into the temp store, so
|
| + // that they will be available to chain building.
|
| + for (X509Certificate::OSCertHandles::const_iterator it =
|
| + intermediates.begin(); it != intermediates.end(); ++it) {
|
| + BOOL added = CertAddCertificateLinkToStore(
|
| + temp_store, *it, CERT_STORE_ADD_USE_EXISTING, NULL);
|
| }
|
|
|
| - // Per MSDN, the resultant structure may be smaller than the original size
|
| - // supplied, so shrink to the actual size output.
|
| - result.resize(size);
|
| + std::vector<PCCERT_CONTEXT> to_add;
|
| + FILETIME now = base::Time::Now().ToFileTime();
|
| + CERT_CHAIN_PARA chain_params;
|
| + memset(&chain_params, 0, sizeof(chain_params));
|
| + chain_params.cbSize = sizeof(chain_params);
|
|
|
| - return result;
|
| -}
|
| + // For every certificate, attempt to build a chain to an existing trust
|
| + // root, where each certificate is minimally valid to be used as a CA
|
| + // certificate (measured by adherance to basicConstraints restrictions,
|
| + // if any, not by key usage)
|
| + size_t offset = 0;
|
| + for (X509Certificate::OSCertHandles::const_iterator it =
|
| + intermediates.begin(); it != intermediates.end(); ++it, ++offset) {
|
| + ScopedPCCERT_CHAIN_CONTEXT chain_context;
|
| + if (!CertGetCertificateChain(NULL, *it, &now, temp_store, &chain_params,
|
| + CERT_CHAIN_CACHE_END_CERT |
|
| + CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE,
|
| + NULL, chain_context.receive())) {
|
| + DPLOG(WARNING) << "Unable to get the certificate chain for "
|
| + << "intermediate " << offset << ". Skipping import of "
|
| + << "this certificate.";
|
| + continue;
|
| + }
|
|
|
| -// Returns true if |cert| was successfully modified to reference |location| to
|
| -// obtain the associated private key.
|
| -bool LinkCertToPrivateKey(X509Certificate* cert,
|
| - KeygenHandler::KeyLocation location) {
|
| - DCHECK(cert);
|
| + CERT_CHAIN_POLICY_PARA chain_policy_params;
|
| + memset(&chain_policy_params, 0, sizeof(chain_policy_params));
|
| + chain_policy_params.cbSize = sizeof(chain_policy_params);
|
| + chain_policy_params.dwFlags =
|
| + BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_CA_FLAG;
|
|
|
| - CRYPT_KEY_PROV_INFO prov_info = { 0 };
|
| - prov_info.pwszContainerName =
|
| - const_cast<LPWSTR>(location.container_name.c_str());
|
| - prov_info.pwszProvName =
|
| - const_cast<LPWSTR>(location.provider_name.c_str());
|
| + CERT_CHAIN_POLICY_STATUS chain_policy_status;
|
| + memset(&chain_policy_status, 0, sizeof(chain_policy_status));
|
| + chain_policy_status.cbSize = sizeof(chain_policy_status);
|
|
|
| - // Implicit by it being from KeygenHandler, which only supports RSA keys.
|
| - prov_info.dwProvType = PROV_RSA_FULL;
|
| - prov_info.dwKeySpec = AT_KEYEXCHANGE;
|
| + // TODO(rsleevi): In the event of bridge/federated chains, where
|
| + // chain_context contains more than one SIMPLE_CHAINS, check what
|
| + // the result will be if the chain context is:
|
| + // cChain = 2
|
| + // rgpChain[0] = Valid chain
|
| + // rgpChain[1] = Invalid chain
|
| + if (!CertVerifyCertificateChainPolicy(
|
| + CERT_CHAIN_POLICY_BASIC_CONSTRAINTS, chain_context,
|
| + &chain_policy_params, &chain_policy_status)) {
|
| + DPLOG(WARNING) << "Unable to get validate the certificate chain for "
|
| + << "intermediate " << offset << ". Skipping import of "
|
| + << "this certificate.";
|
| + continue;
|
| + }
|
|
|
| - BOOL ok = CertSetCertificateContextProperty(cert->os_cert_handle(),
|
| - CERT_KEY_PROV_INFO_PROP_ID, 0,
|
| - &prov_info);
|
| - return ok != FALSE;
|
| + if (chain_policy_status.dwError != 0) {
|
| + DLOG(WARNING) << "Chain for intermediate " << offset << " returned: "
|
| + << std::endl << " Error: " << chain_policy_status.dwError
|
| + << std::endl << " Chain Index: "
|
| + << chain_policy_status.lChainIndex
|
| + << std::endl << " Element Index: "
|
| + << chain_policy_status.lElementIndex;
|
| + continue;
|
| + }
|
| +
|
| + // With CryptoAPI, there may be multiple chains for a given certificate,
|
| + // such as the case with bridged CAs. For each chain, iterate through the
|
| + // certificates that make up the chain. If the certificate was one of the
|
| + // supplied intermediates, then it is appropriate to add to the
|
| + // intermediate list.
|
| + for (DWORD i = 0; i < chain_context.get()->cChain; ++i) {
|
| + for (DWORD j = 0; j < chain_context.get()->rgpChain[i]->cElement; ++j) {
|
| + PCCERT_CONTEXT cert_in_temp_store = CertFindCertificateInStore(
|
| + temp_store.get(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
|
| + CERT_FIND_EXISTING,
|
| + chain_context.get()->rgpChain[i]->rgpElement[j], NULL);
|
| + if (cert_in_temp_store != NULL)
|
| + to_add.push_back(cert_in_temp_store);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // After sifting through all of |intermediates|, add any certificates that
|
| + // passed the checks.
|
| + if (!to_add.empty()) {
|
| + ScopedHCERTSTORE intermediate_db(CertOpenSystemStore(NULL, L"CA"));
|
| + if (intermediate_db) {
|
| + for (std::vector<PCCERT_CONTEXT>::iterator it = to_add.begin();
|
| + it != to_add.end(); ++it) {
|
| + CertAddCertificateContextToStore(intermediate_db, *it,
|
| + CERT_STORE_ADD_USE_EXISTING, NULL);
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (std::vector<PCCERT_CONTEXT>::iterator it = to_add.begin();
|
| + it != to_add.end(); ++it) {
|
| + BOOL freed = CertFreeCertificateContext(*it);
|
| + DCHECK(freed);
|
| + }
|
| }
|
|
|
| } // namespace
|
| @@ -85,12 +161,9 @@
|
| if (cert->HasExpired())
|
| return ERR_CERT_DATE_INVALID;
|
|
|
| - std::string encoded_info = GetSubjectPublicKeyInfo(cert);
|
| - KeygenHandler::Cache* cache = KeygenHandler::Cache::GetInstance();
|
| - KeygenHandler::KeyLocation location;
|
| -
|
| - if (encoded_info.empty() || !cache->Find(encoded_info, &location) ||
|
| - !LinkCertToPrivateKey(cert, location))
|
| + BOOL found = CryptFindCertificateKeyProvInfo(cert->os_cert_handle(),
|
| + 0, NULL);
|
| + if (!found)
|
| return ERR_NO_PRIVATE_KEY_FOR_CERT;
|
|
|
| return OK;
|
| @@ -102,7 +175,7 @@
|
| // NSS, and Store Type / Path) here? For now, certs will be stashed into the
|
| // user's personal store, which will not automatically mark them as trusted,
|
| // but will allow them to be used for client auth.
|
| - HCERTSTORE cert_db = CertOpenSystemStore(NULL, L"MY");
|
| + ScopedHCERTSTORE cert_db(CertOpenSystemStore(NULL, L"MY"));
|
| if (!cert_db)
|
| return ERR_ADD_USER_CERT_FAILED;
|
|
|
| @@ -111,7 +184,7 @@
|
| CERT_STORE_ADD_USE_EXISTING,
|
| NULL);
|
|
|
| - CertCloseStore(cert_db, 0);
|
| + ImportValidIntermediates(cert->GetIntermediateCertificates());
|
|
|
| if (!added)
|
| return ERR_ADD_USER_CERT_FAILED;
|
|
|