Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: base/signaturevalidator.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/signaturevalidator.h ('k') | base/signaturevalidator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2002-2009 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15
16
17 #include "omaha/base/signaturevalidator.h"
18
19 #include <atltime.h>
20 #include <softpub.h>
21 #include <wincrypt.h>
22 #include <wintrust.h>
23 #pragma warning(push)
24 // C4100: unreferenced formal parameter
25 // C4310: cast truncates constant value
26 // C4548: expression before comma has no effect
27 #pragma warning(disable : 4100 4310 4548)
28 #include "base/basictypes.h"
29 #pragma warning(pop)
30 #include "omaha/base/constants.h"
31 #include "omaha/base/error.h"
32
33 namespace omaha {
34
35 namespace {
36
37 const LPCTSTR kEmptyStr = _T("");
38 const DWORD kCertificateEncoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
39
40 // Gets a handle to the certificate store and optionally the cryptographic
41 // message from the specified file.
42 // The caller is responsible for closing the store and message.
43 // message can be NULL if the handle is not needed.
44 HRESULT GetCertStoreFromFile(const wchar_t* signed_file,
45 HCERTSTORE* cert_store,
46 HCRYPTMSG* message) {
47 if (!signed_file || !cert_store) {
48 return E_INVALIDARG;
49 }
50
51 // Get message handle and store handle from the signed file.
52 if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE,
53 signed_file,
54 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
55 CERT_QUERY_FORMAT_FLAG_BINARY,
56 0, // reserved, must be 0
57 NULL, // pdwMsgAndCertEncodingType
58 NULL, // pdwContentType
59 NULL, // pdwFormatType
60 cert_store,
61 message,
62 NULL)) { // ppvContext
63 return HRESULT_FROM_WIN32(::GetLastError());
64 }
65
66 return S_OK;
67 }
68
69 // Gets the signer info from the crypt message.
70 // The caller is responsible for freeing the signer info using LocalFree.
71 HRESULT GetSignerInfo(HCRYPTMSG message, PCMSG_SIGNER_INFO* signer_info) {
72 if (!signer_info) {
73 return E_INVALIDARG;
74 }
75 *signer_info = NULL;
76
77 DWORD info_size = 0;
78 if (!::CryptMsgGetParam(message,
79 CMSG_SIGNER_INFO_PARAM,
80 0,
81 NULL,
82 &info_size)) {
83 return HRESULT_FROM_WIN32(::GetLastError());
84 }
85
86 *signer_info = static_cast<PCMSG_SIGNER_INFO>(::LocalAlloc(LPTR, info_size));
87 if (!*signer_info) {
88 return HRESULT_FROM_WIN32(::GetLastError());
89 }
90
91 if (!::CryptMsgGetParam(message,
92 CMSG_SIGNER_INFO_PARAM,
93 0,
94 *signer_info,
95 &info_size)) {
96 return HRESULT_FROM_WIN32(::GetLastError());
97 }
98
99 return S_OK;
100 }
101
102 // Gets the signer info for the time stamp signature in the specified signature.
103 HRESULT GetTimeStampSignerInfo(PCMSG_SIGNER_INFO signer_info,
104 PCMSG_SIGNER_INFO* countersigner_info) {
105 if (!signer_info || !countersigner_info) {
106 return E_INVALIDARG;
107 }
108 *countersigner_info = NULL;
109
110 PCRYPT_ATTRIBUTE attr = NULL;
111
112 // The countersigner info is contained in the unauthenticated attributes and
113 // indicated by the szOID_RSA_counterSign OID.
114 for (size_t i = 0; i < signer_info->UnauthAttrs.cAttr; ++i) {
115 if (lstrcmpA(szOID_RSA_counterSign,
116 signer_info->UnauthAttrs.rgAttr[i].pszObjId) == 0) {
117 attr = &signer_info->UnauthAttrs.rgAttr[i];
118 break;
119 }
120 }
121
122 if (!attr) {
123 return E_FAIL;
124 }
125
126 // Decode and get CMSG_SIGNER_INFO structure for the timestamp certificate.
127 DWORD data_size = 0;
128 if (!::CryptDecodeObject(kCertificateEncoding,
129 PKCS7_SIGNER_INFO,
130 attr->rgValue[0].pbData,
131 attr->rgValue[0].cbData,
132 0,
133 NULL,
134 &data_size)) {
135 return HRESULT_FROM_WIN32(::GetLastError());
136 }
137
138 *countersigner_info =
139 static_cast<PCMSG_SIGNER_INFO>(::LocalAlloc(LPTR, data_size));
140 if (!*countersigner_info) {
141 return HRESULT_FROM_WIN32(::GetLastError());
142 }
143
144 if (!::CryptDecodeObject(kCertificateEncoding,
145 PKCS7_SIGNER_INFO,
146 attr->rgValue[0].pbData,
147 attr->rgValue[0].cbData,
148 0,
149 *countersigner_info,
150 &data_size)) {
151 return HRESULT_FROM_WIN32(::GetLastError());
152 }
153
154 return S_OK;
155 }
156
157 // Gets the time of the date stamp for the specified signature.
158 // The time is in UTC.
159 HRESULT GetDateOfTimeStamp(PCMSG_SIGNER_INFO signer_info,
160 SYSTEMTIME* system_time) {
161 if (!signer_info || !system_time) {
162 return E_INVALIDARG;
163 }
164
165 PCRYPT_ATTRIBUTE attr = NULL;
166
167 // The signing time is contained in the authenticated attributes and
168 // indicated by the szOID_RSA_signingTime OID.
169 for (size_t i = 0; i < signer_info->AuthAttrs.cAttr; ++i) {
170 if (lstrcmpA(szOID_RSA_signingTime,
171 signer_info->AuthAttrs.rgAttr[i].pszObjId) == 0) {
172 attr = &signer_info->AuthAttrs.rgAttr[i];
173 break;
174 }
175 }
176
177 if (!attr) {
178 return E_FAIL;
179 }
180
181 FILETIME file_time = {0};
182
183 // Decode and get FILETIME structure.
184 DWORD data_size = sizeof(file_time);
185 if (!::CryptDecodeObject(kCertificateEncoding,
186 szOID_RSA_signingTime,
187 attr->rgValue[0].pbData,
188 attr->rgValue[0].cbData,
189 0,
190 &file_time,
191 &data_size)) {
192 return HRESULT_FROM_WIN32(::GetLastError());
193 }
194
195 if (!::FileTimeToSystemTime(&file_time, system_time)) {
196 return HRESULT_FROM_WIN32(::GetLastError());
197 }
198
199 return S_OK;
200 }
201
202 } // namespace
203
204 CertInfo::CertInfo(const CERT_CONTEXT* given_cert_context)
205 : cert_context_(NULL) {
206 if (given_cert_context) {
207 // CertDuplicateCertificateContext just increases reference count of a given
208 // CERT_CONTEXT.
209 cert_context_ = CertDuplicateCertificateContext(given_cert_context);
210 not_valid_before_ = cert_context_->pCertInfo->NotBefore;
211 not_valid_after_ = cert_context_->pCertInfo->NotAfter;
212 // Extract signed party details.
213 ExtractIssuerInfo(cert_context_,
214 &issuing_company_name_,
215 &issuing_dept_name_,
216 &trust_authority_name_);
217 }
218 }
219
220 CertInfo::~CertInfo() {
221 // Decrement reference count, if needed.
222 if (cert_context_)
223 CertFreeCertificateContext(cert_context_);
224 }
225
226
227 bool CertInfo::IsValidNow() const {
228 // we cannot directly get current time in FILETIME format.
229 // so first get it in SYSTEMTIME format and convert it into FILETIME.
230 SYSTEMTIME now;
231 GetSystemTime(&now);
232 FILETIME filetime_now;
233 SystemTimeToFileTime(&now, &filetime_now);
234 // CompareFileTime() is a windows function
235 return ((CompareFileTime(&filetime_now, &not_valid_before_) > 0)
236 && (CompareFileTime(&filetime_now, &not_valid_after_) < 0));
237 }
238
239
240 CString CertInfo::FileTimeToString(const FILETIME* ft) {
241 if (ft == NULL)
242 return _T("");
243 SYSTEMTIME st;
244 if (!FileTimeToSystemTime(ft, &st))
245 return _T("");
246
247 // Build a string showing the date and time.
248 CString time_str;
249 time_str.Format(_T("%02d/%02d/%d %02d:%02d"), st.wDay, st.wMonth, st.wYear,
250 st.wHour, st.wMinute);
251 return time_str;
252 }
253
254
255 bool CertInfo::ExtractField(const CERT_CONTEXT* cert_context,
256 const char* field_name,
257 CString* field_value) {
258 if ((!cert_context) || (!field_name) || (!field_value)) {
259 return false;
260 }
261
262 field_value->Empty();
263
264 DWORD num_chars = ::CertGetNameString(cert_context,
265 CERT_NAME_ATTR_TYPE,
266 0,
267 const_cast<char*>(field_name),
268 NULL,
269 0);
270 if (num_chars > 1) {
271 num_chars = ::CertGetNameString(cert_context,
272 CERT_NAME_ATTR_TYPE,
273 0,
274 const_cast<char*>(field_name),
275 CStrBuf(*field_value, num_chars),
276 num_chars);
277 }
278
279 return num_chars > 1 ? true : false;
280 }
281
282
283 bool CertInfo::ExtractIssuerInfo(const CERT_CONTEXT* cert_context,
284 CString* orgn_name,
285 CString* orgn_dept_name,
286 CString* trust_authority) {
287 // trust-authority is optional, so no check.
288 if ((!orgn_name) || (!orgn_dept_name)) {
289 return false;
290 }
291
292 ExtractField(cert_context, szOID_COMMON_NAME, orgn_name);
293 ExtractField(cert_context, szOID_ORGANIZATIONAL_UNIT_NAME, orgn_dept_name);
294 if (trust_authority != NULL) {
295 ExtractField(cert_context, szOID_ORGANIZATION_NAME, trust_authority);
296 }
297
298 return true;
299 }
300
301
302 void CertList::FindFirstCert(CertInfo** result_cert_info,
303 const CString &company_name_to_match,
304 const CString &orgn_unit_to_match,
305 const CString &trust_authority_to_match,
306 bool allow_test_variant,
307 bool check_cert_is_valid_now) {
308 if (!result_cert_info)
309 return;
310 (*result_cert_info) = NULL;
311
312 for (CertInfoList::const_iterator cert_iter = cert_list_.begin();
313 cert_iter != cert_list_.end();
314 ++cert_iter) {
315 // If any of the criteria does not match, continue on to next certificate
316 if (!company_name_to_match.IsEmpty()) {
317 const TCHAR* certificate_company_name =
318 (*cert_iter)->issuing_company_name_;
319 bool names_match = company_name_to_match == certificate_company_name;
320 if (!names_match && allow_test_variant) {
321 CString test_variant = company_name_to_match;
322 test_variant += _T(" (TEST)");
323 names_match = test_variant == certificate_company_name;
324 }
325 if (!names_match)
326 continue;
327 }
328 if (!orgn_unit_to_match.IsEmpty() &&
329 orgn_unit_to_match != (*cert_iter)->issuing_dept_name_)
330 continue;
331 if (!trust_authority_to_match.IsEmpty() &&
332 trust_authority_to_match != (*cert_iter)->trust_authority_name_)
333 continue;
334 // All the criteria matched. But, add only if it is a valid certificate.
335 if (!check_cert_is_valid_now || (*cert_iter)->IsValidNow()) {
336 (*result_cert_info) = (*cert_iter);
337 return;
338 }
339 }
340 }
341
342
343 void ExtractAllCertificatesFromSignature(const wchar_t* signed_file,
344 CertList* cert_list) {
345 if ((!signed_file) || (!cert_list))
346 return;
347
348 DWORD encoding_type = 0, content_type = 0, format_type = 0;
349 // If successful, cert_store will be populated by
350 // a store containing all the certificates related to the file signature.
351 HCERTSTORE cert_store = NULL;
352 BOOL succeeded = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
353 signed_file,
354 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
355 CERT_QUERY_FORMAT_FLAG_ALL,
356 0, // has to be zero as documentation says
357 &encoding_type, // DWORD *pdwMsgAndCertEncodingType,
358 &content_type, // DWORD *pdwContentType,
359 &format_type, // DWORD *pdwFormatType,
360 &cert_store, // HCERTSTORE *phCertStore,
361 NULL, // HCRYPTMSG *phMsg,
362 NULL); // const void** pvContext
363
364 if (succeeded && (cert_store != NULL)) {
365 PCCERT_CONTEXT cert_context_ptr = NULL;
366 while ((cert_context_ptr =
367 CertEnumCertificatesInStore(cert_store, cert_context_ptr))
368 != NULL) {
369 CertInfo* cert_info = new CertInfo(cert_context_ptr);
370 cert_list->AddCertificate(cert_info);
371 }
372 }
373 if (cert_store) {
374 CertCloseStore(cert_store, 0);
375 }
376 return;
377 }
378
379 // Only check the CN. The OU can change.
380 // TODO(omaha): A better way to implement the valid now check would be to add
381 // a parameter to VerifySignature that adds WTD_LIFETIME_SIGNING_FLAG.
382 bool VerifySigneeIsGoogleInternal(const wchar_t* signed_file,
383 bool check_cert_is_valid_now) {
384 CertList cert_list;
385 ExtractAllCertificatesFromSignature(signed_file, &cert_list);
386 if (cert_list.size() > 0) {
387 CertInfo* required_cert = NULL;
388 // now, see if one of the certificates in the signature belongs to Google.
389 cert_list.FindFirstCert(&required_cert,
390 kCertificateSubjectName,
391 CString(),
392 CString(),
393 true,
394 check_cert_is_valid_now);
395 if (required_cert != NULL) {
396 return true;
397 }
398 }
399 return false;
400 }
401
402 // Does not verify that the certificate is currently valid.
403 // VerifySignature verifies that the certificate was valid at signing time as
404 // part of the normal signature verification.
405 bool VerifySigneeIsGoogle(const wchar_t* signed_file) {
406 return VerifySigneeIsGoogleInternal(signed_file, false);
407 }
408
409 HRESULT VerifySignature(const wchar_t* signed_file, bool allow_network_check) {
410 // Don't pop up any windows
411 HWND const kWindowMode = reinterpret_cast<HWND>(INVALID_HANDLE_VALUE);
412
413 // Verify file & certificates
414 GUID verification_type = WINTRUST_ACTION_GENERIC_VERIFY_V2;
415
416 // Info for the file we're going to verify
417 WINTRUST_FILE_INFO file_info = {0};
418 file_info.cbStruct = sizeof(file_info);
419 file_info.pcwszFilePath = signed_file;
420
421 // Info for request to WinVerifyTrust
422 WINTRUST_DATA trust_data;
423 ZeroMemory(&trust_data, sizeof(trust_data));
424 trust_data.cbStruct = sizeof(trust_data);
425 trust_data.dwUIChoice = WTD_UI_NONE; // no graphics
426 // No additional revocation checking -- note that this flag does not
427 // cancel the flag we set in dwProvFlags; it specifies that no -additional-
428 // checks are to be performed beyond the provider-specified ones.
429 trust_data.fdwRevocationChecks = WTD_REVOKE_NONE;
430 trust_data.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
431
432 if (!allow_network_check)
433 trust_data.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
434
435 trust_data.dwUnionChoice = WTD_CHOICE_FILE; // check a file
436 trust_data.pFile = &file_info; // check this file
437
438 // If the trust provider verifies that the subject is trusted for the
439 // specified action, the return value is zero. No other value besides zero
440 // should be considered a successful return.
441 LONG result = ::WinVerifyTrust(kWindowMode, &verification_type, &trust_data);
442 if (result != 0) {
443 return FAILED(result) ? result : HRESULT_FROM_WIN32(result);
444 }
445 return S_OK;
446 }
447
448 // This method must not return until the end to avoid leaking memory.
449 // More info on Authenticode Signatures Time Stamping can be found at
450 // http://msdn2.microsoft.com/en-us/library/bb931395.aspx.
451 HRESULT GetSigningTime(const wchar_t* signed_file, SYSTEMTIME* signing_time) {
452 if (!signed_file || !signing_time) {
453 return E_INVALIDARG;
454 }
455
456 HCERTSTORE cert_store = NULL;
457 HCRYPTMSG message = NULL;
458 PCMSG_SIGNER_INFO signer_info = NULL;
459 PCMSG_SIGNER_INFO countersigner_info = NULL;
460
461 HRESULT hr = GetCertStoreFromFile(signed_file, &cert_store, &message);
462
463 if (SUCCEEDED(hr)) {
464 hr = GetSignerInfo(message, &signer_info);
465 }
466
467 if (SUCCEEDED(hr)) {
468 hr = GetTimeStampSignerInfo(signer_info, &countersigner_info);
469 }
470
471 if (SUCCEEDED(hr)) {
472 hr = GetDateOfTimeStamp(countersigner_info, signing_time);
473 }
474
475 if (cert_store) {
476 ::CertCloseStore(cert_store, 0);
477 }
478 if (message) {
479 ::CryptMsgClose(message);
480 }
481 ::LocalFree(signer_info);
482 ::LocalFree(countersigner_info);
483
484 return hr;
485 }
486
487 HRESULT VerifyFileSignedWithinDays(const wchar_t* signed_file, int days) {
488 if (!signed_file || days <= 0) {
489 return E_INVALIDARG;
490 }
491
492 SYSTEMTIME signing_time = {0};
493 HRESULT hr = GetSigningTime(signed_file, &signing_time);
494 if (FAILED(hr)) {
495 return hr;
496 }
497
498 // Use the Win32 API instead of CTime::GetCurrentTime() because the latter
499 // is broken in VS 2003 and 2005 and doesn't account for the timezone.
500 SYSTEMTIME current_system_time = {0};
501 ::GetSystemTime(&current_system_time);
502
503 CTime signed_time(signing_time);
504 CTime current_time(current_system_time);
505
506 if (current_time <= signed_time) {
507 return TRUST_E_TIME_STAMP;
508 }
509
510 CTimeSpan time_since_signed = current_time - signed_time;
511 CTimeSpan max_duration(days, 0, 0, 0);
512
513 if (max_duration < time_since_signed) {
514 return TRUST_E_TIME_STAMP;
515 }
516
517 return S_OK;
518 }
519
520 } // namespace omaha
521
OLDNEW
« no previous file with comments | « base/signaturevalidator.h ('k') | base/signaturevalidator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698