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