| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/cert/test_root_certs.h" | 
 |  | 
 | #include "base/basictypes.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/logging.h" | 
 | #include "base/win/win_util.h" | 
 | #include "base/win/windows_version.h" | 
 | #include "net/cert/x509_certificate.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | // Provides a CertDllOpenStoreProv callback provider function, to be called | 
 | // by CertOpenStore when the CERT_STORE_PROV_SYSTEM_W store is opened. See | 
 | // http://msdn.microsoft.com/en-us/library/aa376043(VS.85).aspx. | 
 | BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider, | 
 |                                   DWORD encoding, | 
 |                                   HCRYPTPROV crypt_provider, | 
 |                                   DWORD flags, | 
 |                                   const void* extra, | 
 |                                   HCERTSTORE memory_store, | 
 |                                   PCERT_STORE_PROV_INFO store_info); | 
 |  | 
 | // CryptoAPIInjector is used to inject a store provider function for system | 
 | // certificate stores before the one provided internally by Crypt32.dll. | 
 | // Once injected, there is no way to remove, so every call to open a system | 
 | // store will be redirected to the injected function. | 
 | struct CryptoAPIInjector { | 
 |   // The previous default function for opening system stores. For most | 
 |   // configurations, this should point to Crypt32's internal | 
 |   // I_CertDllOpenSystemStoreProvW function. | 
 |   PFN_CERT_DLL_OPEN_STORE_PROV_FUNC original_function; | 
 |  | 
 |   // The handle that CryptoAPI uses to ensure the DLL implementing | 
 |   // |original_function| remains loaded in memory. | 
 |   HCRYPTOIDFUNCADDR original_handle; | 
 |  | 
 |  private: | 
 |   friend struct base::DefaultLazyInstanceTraits<CryptoAPIInjector>; | 
 |  | 
 |   CryptoAPIInjector() | 
 |       : original_function(NULL), | 
 |         original_handle(NULL) { | 
 |     HCRYPTOIDFUNCSET registered_functions = | 
 |         CryptInitOIDFunctionSet(CRYPT_OID_OPEN_STORE_PROV_FUNC, 0); | 
 |  | 
 |     // Preserve the original handler function in |original_function|. If other | 
 |     // functions are overridden, they will also need to be preserved. | 
 |     BOOL ok = CryptGetOIDFunctionAddress( | 
 |         registered_functions, 0, CERT_STORE_PROV_SYSTEM_W, 0, | 
 |         reinterpret_cast<void**>(&original_function), &original_handle); | 
 |     DCHECK(ok); | 
 |  | 
 |     // For now, intercept only the numeric form of the system store | 
 |     // function, CERT_STORE_PROV_SYSTEM_W (0x0A), which is what Crypt32 | 
 |     // functionality uses exclusively. Depending on the machine that tests | 
 |     // are being run on, it may prove necessary to also intercept | 
 |     // sz_CERT_STORE_PROV_SYSTEM_[A/W] and CERT_STORE_PROV_SYSTEM_A, based | 
 |     // on whether or not any third-party CryptoAPI modules have been | 
 |     // installed. | 
 |     const CRYPT_OID_FUNC_ENTRY kFunctionToIntercept = | 
 |         { CERT_STORE_PROV_SYSTEM_W, &InterceptedOpenStoreW }; | 
 |  | 
 |     // Inject kFunctionToIntercept at the front of the linked list that | 
 |     // crypt32 uses when CertOpenStore is called, replacing the existing | 
 |     // registered function. | 
 |     ok = CryptInstallOIDFunctionAddress(NULL, 0, | 
 |                                         CRYPT_OID_OPEN_STORE_PROV_FUNC, 1, | 
 |                                         &kFunctionToIntercept, | 
 |                                         CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG); | 
 |     DCHECK(ok); | 
 |   } | 
 |  | 
 |   // This is never called, because this object is intentionally leaked. | 
 |   // Certificate verification happens on a non-joinable worker thread, which | 
 |   // may still be running when ~AtExitManager is called, so the LazyInstance | 
 |   // must be leaky. | 
 |   ~CryptoAPIInjector() { | 
 |     original_function = NULL; | 
 |     CryptFreeOIDFunctionAddress(original_handle, NULL); | 
 |   } | 
 | }; | 
 |  | 
 | base::LazyInstance<CryptoAPIInjector>::Leaky | 
 |     g_capi_injector = LAZY_INSTANCE_INITIALIZER; | 
 |  | 
 | BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider, | 
 |                                   DWORD encoding, | 
 |                                   HCRYPTPROV crypt_provider, | 
 |                                   DWORD flags, | 
 |                                   const void* store_name, | 
 |                                   HCERTSTORE memory_store, | 
 |                                   PCERT_STORE_PROV_INFO store_info) { | 
 |   // If the high word is all zeroes, then |store_provider| is a numeric ID. | 
 |   // Otherwise, it's a pointer to a null-terminated ASCII string. See the | 
 |   // documentation for CryptGetOIDFunctionAddress for more information. | 
 |   uint32 store_as_uint = reinterpret_cast<uint32>(store_provider); | 
 |   if (store_as_uint > 0xFFFF || store_provider != CERT_STORE_PROV_SYSTEM_W || | 
 |       !g_capi_injector.Get().original_function) | 
 |     return FALSE; | 
 |  | 
 |   BOOL ok = g_capi_injector.Get().original_function(store_provider, encoding, | 
 |                                                     crypt_provider, flags, | 
 |                                                     store_name, memory_store, | 
 |                                                     store_info); | 
 |   // Only the Root store should have certificates injected. If | 
 |   // CERT_SYSTEM_STORE_RELOCATE_FLAG is set, then |store_name| points to a | 
 |   // CERT_SYSTEM_STORE_RELOCATE_PARA structure, rather than a | 
 |   // NULL-terminated wide string, so check before making a string | 
 |   // comparison. | 
 |   if (!ok || TestRootCerts::GetInstance()->IsEmpty() || | 
 |       (flags & CERT_SYSTEM_STORE_RELOCATE_FLAG) || | 
 |       lstrcmpiW(reinterpret_cast<LPCWSTR>(store_name), L"root")) | 
 |     return ok; | 
 |  | 
 |   // The result of CertOpenStore with CERT_STORE_PROV_SYSTEM_W is documented | 
 |   // to be a collection store, and that appears to hold for |memory_store|. | 
 |   // Attempting to add an individual certificate to |memory_store| causes | 
 |   // the request to be forwarded to the first physical store in the | 
 |   // collection that accepts modifications, which will cause a secure | 
 |   // confirmation dialog to be displayed, confirming the user wishes to | 
 |   // trust the certificate. However, appending a store to the collection | 
 |   // will merely modify the temporary collection store, and will not persist | 
 |   // any changes to the underlying physical store. When the |memory_store| is | 
 |   // searched to see if a certificate is in the Root store, all the | 
 |   // underlying stores in the collection will be searched, and any certificate | 
 |   // in temporary_roots() will be found and seen as trusted. | 
 |   return CertAddStoreToCollection( | 
 |       memory_store, TestRootCerts::GetInstance()->temporary_roots(), 0, 0); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool TestRootCerts::Add(X509Certificate* certificate) { | 
 |   // Ensure that the default CryptoAPI functionality has been intercepted. | 
 |   // If a test certificate is never added, then no interception should | 
 |   // happen. | 
 |   g_capi_injector.Get(); | 
 |  | 
 |   BOOL ok = CertAddCertificateContextToStore( | 
 |       temporary_roots_, certificate->os_cert_handle(), | 
 |       CERT_STORE_ADD_NEW, NULL); | 
 |   if (!ok) { | 
 |     // If the certificate is already added, return successfully. | 
 |     return GetLastError() == CRYPT_E_EXISTS; | 
 |   } | 
 |  | 
 |   empty_ = false; | 
 |   return true; | 
 | } | 
 |  | 
 | void TestRootCerts::Clear() { | 
 |   empty_ = true; | 
 |  | 
 |   for (PCCERT_CONTEXT prev_cert = | 
 |            CertEnumCertificatesInStore(temporary_roots_, NULL); | 
 |        prev_cert; | 
 |        prev_cert = CertEnumCertificatesInStore(temporary_roots_, NULL)) | 
 |     CertDeleteCertificateFromStore(prev_cert); | 
 | } | 
 |  | 
 | bool TestRootCerts::IsEmpty() const { | 
 |   return empty_; | 
 | } | 
 |  | 
 | HCERTCHAINENGINE TestRootCerts::GetChainEngine() const { | 
 |   if (IsEmpty()) | 
 |     return NULL;  // Default chain engine will suffice. | 
 |  | 
 |   // Windows versions before 7 don't accept the struct size for later versions. | 
 |   // We report the size of the old struct since we don't need the new members. | 
 |   static const DWORD kSizeofCertChainEngineConfig = | 
 |       SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER( | 
 |           CERT_CHAIN_ENGINE_CONFIG, CycleDetectionModulus); | 
 |  | 
 |   // Each HCERTCHAINENGINE caches both the configured system stores and | 
 |   // information about each chain that has been built. In order to ensure | 
 |   // that changes to |temporary_roots_| are properly propagated and that the | 
 |   // various caches are flushed, when at least one certificate is added, | 
 |   // return a new chain engine for every call. Each chain engine creation | 
 |   // should re-open the root store, ensuring the most recent changes are | 
 |   // visible. | 
 |   CERT_CHAIN_ENGINE_CONFIG engine_config = { | 
 |     kSizeofCertChainEngineConfig | 
 |   }; | 
 |   engine_config.dwFlags = | 
 |       CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE | | 
 |       CERT_CHAIN_ENABLE_SHARE_STORE; | 
 |   HCERTCHAINENGINE chain_engine = NULL; | 
 |   BOOL ok = CertCreateCertificateChainEngine(&engine_config, &chain_engine); | 
 |   DCHECK(ok); | 
 |   return chain_engine; | 
 | } | 
 |  | 
 | TestRootCerts::~TestRootCerts() { | 
 |   CertCloseStore(temporary_roots_, 0); | 
 | } | 
 |  | 
 | void TestRootCerts::Init() { | 
 |   empty_ = true; | 
 |   temporary_roots_ = CertOpenStore( | 
 |       CERT_STORE_PROV_MEMORY, 0, NULL, | 
 |       CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL); | 
 |   DCHECK(temporary_roots_); | 
 | } | 
 |  | 
 | }  // namespace net |