test_root_certs_win.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. #include "crypto/scoped_capi_types.h"
  5. #include "net/cert/test_root_certs.h"
  6. #include <stdint.h>
  7. #include "base/check.h"
  8. #include "base/lazy_instance.h"
  9. #include "base/numerics/safe_conversions.h"
  10. #include "base/win/win_util.h"
  11. #include "net/cert/x509_certificate.h"
  12. #include "third_party/boringssl/src/include/openssl/pool.h"
  13. namespace net {
  14. namespace {
  15. // Provides a CertDllOpenStoreProv callback provider function, to be called
  16. // by CertOpenStore when the CERT_STORE_PROV_SYSTEM_W store is opened. See
  17. // http://msdn.microsoft.com/en-us/library/aa376043(VS.85).aspx.
  18. BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider,
  19. DWORD encoding,
  20. HCRYPTPROV crypt_provider,
  21. DWORD flags,
  22. const void* extra,
  23. HCERTSTORE memory_store,
  24. PCERT_STORE_PROV_INFO store_info);
  25. // CryptoAPIInjector is used to inject a store provider function for system
  26. // certificate stores before the one provided internally by Crypt32.dll.
  27. // Once injected, there is no way to remove, so every call to open a system
  28. // store will be redirected to the injected function.
  29. struct CryptoAPIInjector {
  30. // The previous default function for opening system stores. For most
  31. // configurations, this should point to Crypt32's internal
  32. // I_CertDllOpenSystemStoreProvW function.
  33. PFN_CERT_DLL_OPEN_STORE_PROV_FUNC original_function;
  34. // The handle that CryptoAPI uses to ensure the DLL implementing
  35. // |original_function| remains loaded in memory.
  36. HCRYPTOIDFUNCADDR original_handle;
  37. private:
  38. friend struct base::LazyInstanceTraitsBase<CryptoAPIInjector>;
  39. CryptoAPIInjector() : original_function(nullptr), original_handle(nullptr) {
  40. HCRYPTOIDFUNCSET registered_functions =
  41. CryptInitOIDFunctionSet(CRYPT_OID_OPEN_STORE_PROV_FUNC, 0);
  42. // Preserve the original handler function in |original_function|. If other
  43. // functions are overridden, they will also need to be preserved.
  44. BOOL ok = CryptGetOIDFunctionAddress(
  45. registered_functions, 0, CERT_STORE_PROV_SYSTEM_W, 0,
  46. reinterpret_cast<void**>(&original_function), &original_handle);
  47. DCHECK(ok);
  48. // For now, intercept only the numeric form of the system store
  49. // function, CERT_STORE_PROV_SYSTEM_W (0x0A), which is what Crypt32
  50. // functionality uses exclusively. Depending on the machine that tests
  51. // are being run on, it may prove necessary to also intercept
  52. // sz_CERT_STORE_PROV_SYSTEM_[A/W] and CERT_STORE_PROV_SYSTEM_A, based
  53. // on whether or not any third-party CryptoAPI modules have been
  54. // installed.
  55. const CRYPT_OID_FUNC_ENTRY kFunctionToIntercept = {
  56. CERT_STORE_PROV_SYSTEM_W,
  57. reinterpret_cast<void*>(&InterceptedOpenStoreW)};
  58. // Inject kFunctionToIntercept at the front of the linked list that
  59. // crypt32 uses when CertOpenStore is called, replacing the existing
  60. // registered function.
  61. ok = CryptInstallOIDFunctionAddress(
  62. nullptr, 0, CRYPT_OID_OPEN_STORE_PROV_FUNC, 1, &kFunctionToIntercept,
  63. CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG);
  64. DCHECK(ok);
  65. }
  66. // This is never called, because this object is intentionally leaked.
  67. // Certificate verification happens on a non-joinable worker thread, which
  68. // may still be running when ~AtExitManager is called, so the LazyInstance
  69. // must be leaky.
  70. ~CryptoAPIInjector() {
  71. original_function = nullptr;
  72. CryptFreeOIDFunctionAddress(original_handle, NULL);
  73. }
  74. };
  75. base::LazyInstance<CryptoAPIInjector>::Leaky
  76. g_capi_injector = LAZY_INSTANCE_INITIALIZER;
  77. BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider,
  78. DWORD encoding,
  79. HCRYPTPROV crypt_provider,
  80. DWORD flags,
  81. const void* store_name,
  82. HCERTSTORE memory_store,
  83. PCERT_STORE_PROV_INFO store_info) {
  84. // If the high word is all zeroes, then |store_provider| is a numeric ID.
  85. // Otherwise, it's a pointer to a null-terminated ASCII string. See the
  86. // documentation for CryptGetOIDFunctionAddress for more information.
  87. uintptr_t store_as_uintptr = reinterpret_cast<uintptr_t>(store_provider);
  88. if (store_as_uintptr > 0xFFFF || store_provider != CERT_STORE_PROV_SYSTEM_W ||
  89. !g_capi_injector.Get().original_function)
  90. return FALSE;
  91. BOOL ok = g_capi_injector.Get().original_function(store_provider, encoding,
  92. crypt_provider, flags,
  93. store_name, memory_store,
  94. store_info);
  95. // Only the Root store should have certificates injected. If
  96. // CERT_SYSTEM_STORE_RELOCATE_FLAG is set, then |store_name| points to a
  97. // CERT_SYSTEM_STORE_RELOCATE_PARA structure, rather than a
  98. // NULL-terminated wide string, so check before making a string
  99. // comparison.
  100. if (!ok || TestRootCerts::GetInstance()->IsEmpty() ||
  101. (flags & CERT_SYSTEM_STORE_RELOCATE_FLAG) ||
  102. lstrcmpiW(reinterpret_cast<LPCWSTR>(store_name), L"root"))
  103. return ok;
  104. // The result of CertOpenStore with CERT_STORE_PROV_SYSTEM_W is documented
  105. // to be a collection store, and that appears to hold for |memory_store|.
  106. // Attempting to add an individual certificate to |memory_store| causes
  107. // the request to be forwarded to the first physical store in the
  108. // collection that accepts modifications, which will cause a secure
  109. // confirmation dialog to be displayed, confirming the user wishes to
  110. // trust the certificate. However, appending a store to the collection
  111. // will merely modify the temporary collection store, and will not persist
  112. // any changes to the underlying physical store. When the |memory_store| is
  113. // searched to see if a certificate is in the Root store, all the
  114. // underlying stores in the collection will be searched, and any certificate
  115. // in temporary_roots() will be found and seen as trusted.
  116. return CertAddStoreToCollection(
  117. memory_store, TestRootCerts::GetInstance()->temporary_roots(), 0, 0);
  118. }
  119. } // namespace
  120. bool TestRootCerts::AddImpl(X509Certificate* certificate) {
  121. // Ensure that the default CryptoAPI functionality has been intercepted.
  122. // If a test certificate is never added, then no interception should
  123. // happen.
  124. g_capi_injector.Get();
  125. BOOL ok = CertAddEncodedCertificateToStore(
  126. temporary_roots_, X509_ASN_ENCODING,
  127. reinterpret_cast<const BYTE*>(
  128. CRYPTO_BUFFER_data(certificate->cert_buffer())),
  129. base::checked_cast<DWORD>(CRYPTO_BUFFER_len(certificate->cert_buffer())),
  130. CERT_STORE_ADD_NEW, nullptr);
  131. if (!ok) {
  132. // If the certificate is already added, return successfully.
  133. return GetLastError() == static_cast<DWORD>(CRYPT_E_EXISTS);
  134. }
  135. return true;
  136. }
  137. void TestRootCerts::ClearImpl() {
  138. for (PCCERT_CONTEXT prev_cert =
  139. CertEnumCertificatesInStore(temporary_roots_, nullptr);
  140. prev_cert;
  141. prev_cert = CertEnumCertificatesInStore(temporary_roots_, nullptr))
  142. CertDeleteCertificateFromStore(prev_cert);
  143. }
  144. crypto::ScopedHCERTCHAINENGINE TestRootCerts::GetChainEngine() const {
  145. if (IsEmpty()) {
  146. // Default chain engine will suffice.
  147. return crypto::ScopedHCERTCHAINENGINE();
  148. }
  149. // Windows versions before 8 don't accept the struct size for later versions.
  150. // We report the size of the old struct since we don't need the new members.
  151. static const DWORD kSizeofCertChainEngineConfig =
  152. SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(CERT_CHAIN_ENGINE_CONFIG,
  153. hExclusiveTrustedPeople);
  154. // Each HCERTCHAINENGINE caches both the configured system stores and
  155. // information about each chain that has been built. In order to ensure
  156. // that changes to |temporary_roots_| are properly propagated and that the
  157. // various caches are flushed, when at least one certificate is added,
  158. // return a new chain engine for every call. Each chain engine creation
  159. // should re-open the root store, ensuring the most recent changes are
  160. // visible.
  161. CERT_CHAIN_ENGINE_CONFIG engine_config = {
  162. kSizeofCertChainEngineConfig
  163. };
  164. engine_config.dwFlags =
  165. CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE |
  166. CERT_CHAIN_ENABLE_SHARE_STORE;
  167. crypto::ScopedHCERTCHAINENGINE chain_engine;
  168. BOOL ok = CertCreateCertificateChainEngine(
  169. &engine_config,
  170. crypto::ScopedHCERTCHAINENGINE::Receiver(chain_engine).get());
  171. DCHECK(ok);
  172. return chain_engine;
  173. }
  174. TestRootCerts::~TestRootCerts() {
  175. CertCloseStore(temporary_roots_, 0);
  176. }
  177. void TestRootCerts::Init() {
  178. temporary_roots_ =
  179. CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL,
  180. CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr);
  181. DCHECK(temporary_roots_);
  182. }
  183. } // namespace net