1// Copyright 2013 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 5#include "chrome/app/signature_validator_win.h" 6 7#include <atlstr.h> 8#include <softpub.h> 9#include <wincrypt.h> 10#include <windows.h> 11#include <wintrust.h> 12 13#include <algorithm> 14 15#include "base/files/file_path.h" 16#include "base/strings/string_number_conversions.h" 17#include "base/strings/string_util.h" 18#include "base/strings/utf_string_conversions.h" 19#include "base/time/time.h" 20#include "base/win/scoped_handle.h" 21#include "crypto/sha2.h" 22 23namespace { 24 25bool ExtractPublicKeyHash(const CERT_CONTEXT* cert_context, 26 std::string* public_key_hash) { 27 public_key_hash->clear(); 28 29 CRYPT_BIT_BLOB crypt_blob = 30 cert_context->pCertInfo->SubjectPublicKeyInfo.PublicKey; 31 32 // Key blobs that are not an integral number of bytes are unsupported. 33 if (crypt_blob.cUnusedBits != 0) 34 return false; 35 36 uint8 hash[crypto::kSHA256Length] = {}; 37 38 base::StringPiece key_bytes(reinterpret_cast<char*>( 39 crypt_blob.pbData), crypt_blob.cbData); 40 crypto::SHA256HashString(key_bytes, hash, crypto::kSHA256Length); 41 42 *public_key_hash = 43 base::StringToLowerASCII(base::HexEncode(hash, arraysize(hash))); 44 return true; 45} 46 47// The traits class for HCERTSTORE handles that can be closed via 48// CertCloseStore() API. 49class CertStoreHandleTraits { 50 public: 51 typedef HCERTSTORE Handle; 52 53 // Closes the handle. 54 static bool CloseHandle(HCERTSTORE handle) { 55 return CertCloseStore(handle, 0) != FALSE; 56 } 57 58 // Returns true if the handle value is valid. 59 static bool IsHandleValid(HCERTSTORE handle) { 60 return handle != NULL; 61 } 62 63 // Returns NULL handle value. 64 static HANDLE NullHandle() { 65 return NULL; 66 } 67 68 private: 69 DISALLOW_IMPLICIT_CONSTRUCTORS(CertStoreHandleTraits); 70}; 71 72typedef base::win::GenericScopedHandle<CertStoreHandleTraits, 73 base::win::DummyVerifierTraits> ScopedCertStoreHandle; 74 75// Class: CertInfo 76// 77// CertInfo holds all sensible details of a certificate. During verification of 78// a signature, one CertInfo object is made for each certificate encountered in 79// the signature. 80class CertInfo { 81 public: 82 explicit CertInfo(const CERT_CONTEXT* given_cert_context) 83 : cert_context_(NULL) { 84 if (given_cert_context) { 85 // CertDuplicateCertificateContext just increases reference count of a 86 // given CERT_CONTEXT. 87 cert_context_ = CertDuplicateCertificateContext(given_cert_context); 88 not_valid_before_ = cert_context_->pCertInfo->NotBefore; 89 not_valid_after_ = cert_context_->pCertInfo->NotAfter; 90 91 ExtractPublicKeyHash(cert_context_, &public_key_hash_); 92 } 93 } 94 95 ~CertInfo() { // Decrement reference count, if needed. 96 if (cert_context_) { 97 CertFreeCertificateContext(cert_context_); 98 cert_context_ = NULL; 99 } 100 } 101 102 // IsValidNow() functions returns true if this certificate is valid at this 103 // moment, based on the validity period specified in the certificate. 104 bool IsValidNow() const { 105 // we cannot directly get current time in FILETIME format. 106 // so first get it in SYSTEMTIME format and convert it into FILETIME. 107 base::Time now = base::Time::NowFromSystemTime(); 108 FILETIME filetime_now = now.ToFileTime(); 109 // CompareFileTime() is a windows function 110 return ((CompareFileTime(&filetime_now, ¬_valid_before_) > 0) && 111 (CompareFileTime(&filetime_now, ¬_valid_after_) < 0)); 112 } 113 114 std::string public_key_hash() const { 115 return public_key_hash_; 116 } 117 118 private: 119 // cert_context structure, defined by Crypto API, contains all the info 120 // about the certificate. 121 const CERT_CONTEXT* cert_context_; 122 123 // Lower-case hex SHA-256 hash of the certificate subject's public key. 124 std::string public_key_hash_; 125 126 // Validity period start-date 127 FILETIME not_valid_before_; 128 129 // Validity period end-date 130 FILETIME not_valid_after_; 131}; 132 133} // namespace 134 135bool VerifyAuthenticodeSignature(const base::FilePath& signed_file) { 136 // Don't pop up any windows 137 const HWND window_mode = reinterpret_cast<HWND>(INVALID_HANDLE_VALUE); 138 139 // Verify file & certificates using the Microsoft Authenticode Policy 140 // Provider. 141 GUID verification_type = WINTRUST_ACTION_GENERIC_VERIFY_V2; 142 143 // Info for the file we're going to verify. 144 WINTRUST_FILE_INFO file_info = {}; 145 file_info.cbStruct = sizeof(file_info); 146 file_info.pcwszFilePath = signed_file.value().c_str(); 147 148 // Info for request to WinVerifyTrust. 149 WINTRUST_DATA trust_data = {}; 150 trust_data.cbStruct = sizeof(trust_data); 151 trust_data.dwUIChoice = WTD_UI_NONE; // no graphics 152 trust_data.fdwRevocationChecks = WTD_REVOKE_NONE; // no revocation checking 153 trust_data.dwUnionChoice = WTD_CHOICE_FILE; // check a file 154 trust_data.pFile = &file_info; // check this file 155 trust_data.dwProvFlags = WTD_REVOCATION_CHECK_NONE; 156 157 // From the WinVerifyTrust documentation: 158 // http://msdn2.microsoft.com/en-us/library/aa388208.aspx: 159 // "If the trust provider verifies that the subject is trusted 160 // for the specified action, the return value is zero. No other 161 // value besides zero should be considered a successful return." 162 LONG result = WinVerifyTrust(window_mode, &verification_type, &trust_data); 163 return !result; 164} 165 166bool VerifySignerIsGoogle(const base::FilePath& signed_file, 167 const std::string& subject_name, 168 const std::vector<std::string>& expected_hashes) { 169 if (signed_file.empty()) 170 return false; 171 172 // If successful, cert_store will be populated by a store containing all the 173 // certificates related to the file signature. 174 HCERTSTORE cert_store = NULL; 175 176 BOOL succeeded = CryptQueryObject( 177 CERT_QUERY_OBJECT_FILE, 178 signed_file.value().c_str(), 179 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, 180 CERT_QUERY_FORMAT_FLAG_ALL, 181 0, // Reserved: must be 0. 182 NULL, 183 NULL, 184 NULL, 185 &cert_store, 186 NULL, // Do not return the message. 187 NULL); // Do not return additional context. 188 189 ScopedCertStoreHandle scoped_cert_store(cert_store); 190 191 if (!succeeded || !scoped_cert_store.IsValid()) 192 return false; 193 194 PCCERT_CONTEXT cert_context_ptr = NULL; 195 cert_context_ptr = CertFindCertificateInStore( 196 cert_store, 197 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 198 0, 199 CERT_FIND_SUBJECT_STR, 200 base::UTF8ToWide(subject_name).c_str(), 201 cert_context_ptr); 202 203 // No cert found with this subject, so stop here. 204 if (!cert_context_ptr) 205 return false; 206 207 CertInfo cert_info(cert_context_ptr); 208 209 CertFreeCertificateContext(cert_context_ptr); 210 cert_context_ptr = NULL; 211 212 // Check the hashes to make sure subject isn't being faked, and the time 213 // to make sure the cert is current. 214 std::vector<std::string>::const_iterator it = std::find( 215 expected_hashes.begin(), 216 expected_hashes.end(), 217 cert_info.public_key_hash()); 218 if (it == expected_hashes.end() || !cert_info.IsValidNow()) 219 return false; 220 221 return true; 222} 223