| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/safe_browsing/signature_util.h" | 5 #include "chrome/browser/safe_browsing/signature_util.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <softpub.h> | 8 #include <softpub.h> |
| 9 #include <wincrypt.h> | |
| 10 #include <wintrust.h> | 9 #include <wintrust.h> |
| 11 | 10 |
| 12 #include "base/file_path.h" | 11 #include "base/file_path.h" |
| 13 #include "base/logging.h" | 12 #include "base/logging.h" |
| 14 #include "base/scoped_ptr.h" | |
| 15 #include "crypto/scoped_capi_types.h" | |
| 16 #include "chrome/common/safe_browsing/csd.pb.h" | 13 #include "chrome/common/safe_browsing/csd.pb.h" |
| 17 | 14 |
| 18 #pragma comment(lib, "crypt32.lib") | |
| 19 #pragma comment(lib, "wintrust.lib") | 15 #pragma comment(lib, "wintrust.lib") |
| 20 | 16 |
| 21 namespace safe_browsing { | 17 namespace safe_browsing { |
| 22 namespace { | |
| 23 using crypto::ScopedCAPIHandle; | |
| 24 using crypto::CAPIDestroyer; | |
| 25 using crypto::CAPIDestroyerWithFlags; | |
| 26 | 18 |
| 27 // Free functors for scoped_ptr_malloc. | 19 SignatureUtil::SignatureUtil() {} |
| 28 class FreeConstCertContext { | |
| 29 public: | |
| 30 void operator()(const CERT_CONTEXT* cert_context) const { | |
| 31 if (cert_context) { | |
| 32 CertFreeCertificateContext(cert_context); | |
| 33 } | |
| 34 } | |
| 35 }; | |
| 36 | 20 |
| 37 class FreeConstCertChainContext { | 21 SignatureUtil::~SignatureUtil() {} |
| 38 public: | |
| 39 void operator()(const CERT_CHAIN_CONTEXT* cert_chain_context) const { | |
| 40 if (cert_chain_context) { | |
| 41 CertFreeCertificateChain(cert_chain_context); | |
| 42 } | |
| 43 } | |
| 44 }; | |
| 45 | 22 |
| 46 // Tries to extract the signing certificate from |file_path|. On success, | 23 void SignatureUtil::CheckSignature( |
| 47 // the |certificate_contents| field of |signature_info| is populated with the | |
| 48 // DER-encoded X.509 certificate. | |
| 49 void GetCertificateContents( | |
| 50 const FilePath& file_path, | 24 const FilePath& file_path, |
| 51 ClientDownloadRequest_SignatureInfo* signature_info) { | 25 ClientDownloadRequest_SignatureInfo* signature_info) { |
| 52 // Largely based on http://support.microsoft.com/kb/323809 | 26 VLOG(2) << "Checking signature for " << file_path.value(); |
| 53 // Get message handle and store handle from the signed file. | |
| 54 typedef CAPIDestroyer<HCRYPTMSG, CryptMsgClose> CryptMsgDestroyer; | |
| 55 ScopedCAPIHandle<HCRYPTMSG, CryptMsgDestroyer> crypt_msg; | |
| 56 typedef CAPIDestroyerWithFlags< | |
| 57 HCERTSTORE, CertCloseStore, 0> CertStoreDestroyer; | |
| 58 ScopedCAPIHandle<HCERTSTORE, CertStoreDestroyer> cert_store; | |
| 59 | 27 |
| 60 VLOG(2) << "Looking for signature in: " << file_path.value(); | |
| 61 if (!CryptQueryObject(CERT_QUERY_OBJECT_FILE, | |
| 62 file_path.value().c_str(), | |
| 63 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, | |
| 64 CERT_QUERY_FORMAT_FLAG_BINARY, | |
| 65 0, // flags | |
| 66 NULL, // encoding | |
| 67 NULL, // content type | |
| 68 NULL, // format type | |
| 69 cert_store.receive(), | |
| 70 crypt_msg.receive(), | |
| 71 NULL)) { // context | |
| 72 VLOG(2) << "No signature found."; | |
| 73 return; | |
| 74 } | |
| 75 | |
| 76 // Get the signer information size. | |
| 77 DWORD signer_info_size; | |
| 78 if (!CryptMsgGetParam(crypt_msg.get(), | |
| 79 CMSG_SIGNER_INFO_PARAM, | |
| 80 0, // index | |
| 81 NULL, // no buffer when checking the size | |
| 82 &signer_info_size)) { | |
| 83 VLOG(2) << "Failed to get signer info size"; | |
| 84 return; | |
| 85 } | |
| 86 | |
| 87 // Get the signer information. | |
| 88 scoped_array<BYTE> signer_info_buffer(new BYTE[signer_info_size]); | |
| 89 CMSG_SIGNER_INFO* signer_info = | |
| 90 reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get()); | |
| 91 if (!CryptMsgGetParam(crypt_msg.get(), | |
| 92 CMSG_SIGNER_INFO_PARAM, | |
| 93 0, // index | |
| 94 signer_info, | |
| 95 &signer_info_size)) { | |
| 96 VLOG(2) << "Failed to get signer info"; | |
| 97 return; | |
| 98 } | |
| 99 | |
| 100 // Search for the signer certificate in the temporary certificate store. | |
| 101 CERT_INFO cert_info; | |
| 102 cert_info.Issuer = signer_info->Issuer; | |
| 103 cert_info.SerialNumber = signer_info->SerialNumber; | |
| 104 | |
| 105 scoped_ptr_malloc<const CERT_CONTEXT, FreeConstCertContext> cert_context( | |
| 106 CertFindCertificateInStore( | |
| 107 cert_store.get(), | |
| 108 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, | |
| 109 0, // flags | |
| 110 CERT_FIND_SUBJECT_CERT, | |
| 111 &cert_info, | |
| 112 NULL)); // no previous context | |
| 113 if (!cert_context.get()) { | |
| 114 VLOG(2) << "Failed to get CERT_CONTEXT"; | |
| 115 return; | |
| 116 } | |
| 117 | |
| 118 // Follow the chain of certificates to a trusted root. | |
| 119 CERT_CHAIN_PARA cert_chain_params; | |
| 120 memset(&cert_chain_params, 0, sizeof(cert_chain_params)); | |
| 121 cert_chain_params.cbSize = sizeof(cert_chain_params); | |
| 122 | |
| 123 const CERT_CHAIN_CONTEXT* cert_chain_context = NULL; | |
| 124 if (!CertGetCertificateChain(NULL, // default chain engine | |
| 125 cert_context.get(), | |
| 126 // TODO(bryner): should this verify at the | |
| 127 // executable's embedded timestamp? | |
| 128 NULL, // verify at the current time | |
| 129 NULL, // no additional store | |
| 130 &cert_chain_params, | |
| 131 0, // default flags | |
| 132 NULL, // reserved parameter | |
| 133 &cert_chain_context)) { | |
| 134 VLOG(2) << "Failed to get certificate chain."; | |
| 135 return; | |
| 136 } | |
| 137 | |
| 138 typedef scoped_ptr_malloc<const CERT_CHAIN_CONTEXT, | |
| 139 FreeConstCertChainContext> ScopedCertChainContext; | |
| 140 ScopedCertChainContext scoped_cert_chain_context(cert_chain_context); | |
| 141 for (size_t i = 0; i < cert_chain_context->cChain; ++i) { | |
| 142 CERT_SIMPLE_CHAIN* simple_chain = cert_chain_context->rgpChain[i]; | |
| 143 ClientDownloadRequest_CertificateChain* chain = | |
| 144 signature_info->add_certificate_chain(); | |
| 145 for (size_t j = 0; j < simple_chain->cElement; ++j) { | |
| 146 CERT_CHAIN_ELEMENT* element = simple_chain->rgpElement[j]; | |
| 147 chain->add_element()->set_certificate( | |
| 148 element->pCertContext->pbCertEncoded, | |
| 149 element->pCertContext->cbCertEncoded); | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 bool CheckTrust(const FilePath& file_path) { | |
| 155 WINTRUST_FILE_INFO file_info; | 28 WINTRUST_FILE_INFO file_info; |
| 156 file_info.cbStruct = sizeof(file_info); | 29 file_info.cbStruct = sizeof(file_info); |
| 157 file_info.pcwszFilePath = file_path.value().c_str(); | 30 file_info.pcwszFilePath = file_path.value().c_str(); |
| 158 file_info.hFile = NULL; | 31 file_info.hFile = NULL; |
| 159 file_info.pgKnownSubject = NULL; | 32 file_info.pgKnownSubject = NULL; |
| 160 | 33 |
| 161 WINTRUST_DATA wintrust_data; | 34 WINTRUST_DATA wintrust_data; |
| 162 wintrust_data.cbStruct = sizeof(wintrust_data); | 35 wintrust_data.cbStruct = sizeof(wintrust_data); |
| 163 wintrust_data.pPolicyCallbackData = NULL; | 36 wintrust_data.pPolicyCallbackData = NULL; |
| 164 wintrust_data.pSIPClientData = NULL; | 37 wintrust_data.pSIPClientData = NULL; |
| 165 wintrust_data.dwUIChoice = WTD_UI_NONE; | 38 wintrust_data.dwUIChoice = WTD_UI_NONE; |
| 166 wintrust_data.fdwRevocationChecks = WTD_REVOKE_NONE; | 39 wintrust_data.fdwRevocationChecks = WTD_REVOKE_NONE; |
| 167 wintrust_data.dwUnionChoice = WTD_CHOICE_FILE; | 40 wintrust_data.dwUnionChoice = WTD_CHOICE_FILE; |
| 168 wintrust_data.pFile = &file_info; | 41 wintrust_data.pFile = &file_info; |
| 169 wintrust_data.dwStateAction = WTD_STATEACTION_IGNORE; | 42 wintrust_data.dwStateAction = WTD_STATEACTION_VERIFY; |
| 170 wintrust_data.hWVTStateData = NULL; | 43 wintrust_data.hWVTStateData = NULL; |
| 171 wintrust_data.pwszURLReference = NULL; | 44 wintrust_data.pwszURLReference = NULL; |
| 172 wintrust_data.dwProvFlags = 0; // don't set any flags | 45 // Disallow revocation checks over the network. |
| 46 wintrust_data.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL; |
| 173 wintrust_data.dwUIContext = WTD_UICONTEXT_EXECUTE; | 47 wintrust_data.dwUIContext = WTD_UICONTEXT_EXECUTE; |
| 174 | 48 |
| 175 // The WINTRUST_ACTION_GENERIC_VERIFY_V2 policy verifies that the certificate | 49 // The WINTRUST_ACTION_GENERIC_VERIFY_V2 policy verifies that the certificate |
| 176 // chains up to a trusted root CA, and that it has appropriate permission to | 50 // chains up to a trusted root CA, and that it has appropriate permission to |
| 177 // sign code. | 51 // sign code. |
| 178 GUID policy_guid = WINTRUST_ACTION_GENERIC_VERIFY_V2; | 52 GUID policy_guid = WINTRUST_ACTION_GENERIC_VERIFY_V2; |
| 179 | 53 |
| 180 return (WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), | 54 LONG result = WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), |
| 181 &policy_guid, | 55 &policy_guid, |
| 182 &wintrust_data) == ERROR_SUCCESS); | 56 &wintrust_data); |
| 183 } | |
| 184 } // namespace | |
| 185 | 57 |
| 186 SignatureUtil::SignatureUtil() {} | 58 CRYPT_PROVIDER_DATA* prov_data = WTHelperProvDataFromStateData( |
| 59 wintrust_data.hWVTStateData); |
| 60 if (prov_data) { |
| 61 if (prov_data->csSigners > 0) { |
| 62 signature_info->set_trusted(result == ERROR_SUCCESS); |
| 63 } |
| 64 for (DWORD i = 0; i < prov_data->csSigners; ++i) { |
| 65 const CERT_CHAIN_CONTEXT* cert_chain_context = |
| 66 prov_data->pasSigners[i].pChainContext; |
| 67 for (DWORD j = 0; j < cert_chain_context->cChain; ++j) { |
| 68 CERT_SIMPLE_CHAIN* simple_chain = cert_chain_context->rgpChain[j]; |
| 69 ClientDownloadRequest_CertificateChain* chain = |
| 70 signature_info->add_certificate_chain(); |
| 71 for (DWORD k = 0; k < simple_chain->cElement; ++k) { |
| 72 CERT_CHAIN_ELEMENT* element = simple_chain->rgpElement[k]; |
| 73 chain->add_element()->set_certificate( |
| 74 element->pCertContext->pbCertEncoded, |
| 75 element->pCertContext->cbCertEncoded); |
| 76 } |
| 77 } |
| 78 } |
| 187 | 79 |
| 188 SignatureUtil::~SignatureUtil() {} | 80 // Free the provider data. |
| 189 | 81 wintrust_data.dwStateAction = WTD_STATEACTION_CLOSE; |
| 190 void SignatureUtil::CheckSignature( | 82 WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), |
| 191 const FilePath& file_path, | 83 &policy_guid, &wintrust_data); |
| 192 ClientDownloadRequest_SignatureInfo* signature_info) { | 84 } |
| 193 GetCertificateContents(file_path, signature_info); | |
| 194 signature_info->set_trusted(CheckTrust(file_path)); | |
| 195 } | 85 } |
| 196 | 86 |
| 197 } // namespace safe_browsing | 87 } // namespace safe_browsing |
| OLD | NEW |