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 |