| OLD | NEW |
| (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, ¬_valid_before_) > 0) | |
| 236 && (CompareFileTime(&filetime_now, ¬_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(¤t_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 | |
| OLD | NEW |