| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/win/enumerate_modules_model.h" | 5 #include "chrome/browser/win/enumerate_modules_model.h" |
| 6 | 6 |
| 7 #include <softpub.h> | 7 #include <softpub.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <stdint.h> | 9 #include <stdint.h> |
| 10 #include <tlhelp32.h> | 10 #include <tlhelp32.h> |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 DWORD return_value = GetLongPathName(short_path.c_str(), long_path_buf, | 90 DWORD return_value = GetLongPathName(short_path.c_str(), long_path_buf, |
| 91 MAX_PATH); | 91 MAX_PATH); |
| 92 if (return_value != 0 && return_value < MAX_PATH) { | 92 if (return_value != 0 && return_value < MAX_PATH) { |
| 93 *long_path = long_path_buf; | 93 *long_path = long_path_buf; |
| 94 return true; | 94 return true; |
| 95 } | 95 } |
| 96 | 96 |
| 97 return false; | 97 return false; |
| 98 } | 98 } |
| 99 | 99 |
| 100 // Helper for scoped tracking an HCERTSTORE. | |
| 101 struct ScopedHCERTSTORETraits { | |
| 102 static HCERTSTORE InvalidValue() { return nullptr; } | |
| 103 static void Free(HCERTSTORE store) { | |
| 104 ::CertCloseStore(store, 0); | |
| 105 } | |
| 106 }; | |
| 107 using ScopedHCERTSTORE = | |
| 108 base::ScopedGeneric<HCERTSTORE, ScopedHCERTSTORETraits>; | |
| 109 | |
| 110 // Helper for scoped tracking an HCRYPTMSG. | |
| 111 struct ScopedHCRYPTMSGTraits { | |
| 112 static HCRYPTMSG InvalidValue() { return nullptr; } | |
| 113 static void Free(HCRYPTMSG message) { | |
| 114 ::CryptMsgClose(message); | |
| 115 } | |
| 116 }; | |
| 117 using ScopedHCRYPTMSG = | |
| 118 base::ScopedGeneric<HCRYPTMSG, ScopedHCRYPTMSGTraits>; | |
| 119 | |
| 120 // Returns the "Subject" field from the digital signature in the provided | |
| 121 // binary, if any is present. Returns an empty string on failure. | |
| 122 base::string16 GetSubjectNameInFile(const base::FilePath& filename) { | |
| 123 ScopedHCERTSTORE store; | |
| 124 ScopedHCRYPTMSG message; | |
| 125 | |
| 126 // Find the crypto message for this filename. | |
| 127 { | |
| 128 HCERTSTORE temp_store = nullptr; | |
| 129 HCRYPTMSG temp_message = nullptr; | |
| 130 bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE, | |
| 131 filename.value().c_str(), | |
| 132 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, | |
| 133 CERT_QUERY_FORMAT_FLAG_BINARY, | |
| 134 0, | |
| 135 nullptr, | |
| 136 nullptr, | |
| 137 nullptr, | |
| 138 &temp_store, | |
| 139 &temp_message, | |
| 140 nullptr); | |
| 141 store.reset(temp_store); | |
| 142 message.reset(temp_message); | |
| 143 if (!result) | |
| 144 return base::string16(); | |
| 145 } | |
| 146 | |
| 147 // Determine the size of the signer info data. | |
| 148 DWORD signer_info_size = 0; | |
| 149 bool result = !!CryptMsgGetParam(message.get(), | |
| 150 CMSG_SIGNER_INFO_PARAM, | |
| 151 0, | |
| 152 nullptr, | |
| 153 &signer_info_size); | |
| 154 if (!result) | |
| 155 return base::string16(); | |
| 156 | |
| 157 // Allocate enough space to hold the signer info. | |
| 158 std::unique_ptr<BYTE[]> signer_info_buffer(new BYTE[signer_info_size]); | |
| 159 CMSG_SIGNER_INFO* signer_info = | |
| 160 reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get()); | |
| 161 | |
| 162 // Obtain the signer info. | |
| 163 result = !!CryptMsgGetParam(message.get(), | |
| 164 CMSG_SIGNER_INFO_PARAM, | |
| 165 0, | |
| 166 signer_info, | |
| 167 &signer_info_size); | |
| 168 if (!result) | |
| 169 return base::string16(); | |
| 170 | |
| 171 // Search for the signer certificate. | |
| 172 CERT_INFO CertInfo = {0}; | |
| 173 PCCERT_CONTEXT cert_context = nullptr; | |
| 174 CertInfo.Issuer = signer_info->Issuer; | |
| 175 CertInfo.SerialNumber = signer_info->SerialNumber; | |
| 176 | |
| 177 cert_context = CertFindCertificateInStore( | |
| 178 store.get(), | |
| 179 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, | |
| 180 0, | |
| 181 CERT_FIND_SUBJECT_CERT, | |
| 182 &CertInfo, | |
| 183 nullptr); | |
| 184 if (!cert_context) | |
| 185 return base::string16(); | |
| 186 | |
| 187 // Determine the size of the Subject name. | |
| 188 DWORD subject_name_size = CertGetNameString( | |
| 189 cert_context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0); | |
| 190 if (!subject_name_size) | |
| 191 return base::string16(); | |
| 192 | |
| 193 base::string16 subject_name; | |
| 194 subject_name.resize(subject_name_size); | |
| 195 | |
| 196 // Get subject name. | |
| 197 if (!(CertGetNameString(cert_context, | |
| 198 CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| 199 0, | |
| 200 nullptr, | |
| 201 const_cast<LPWSTR>(subject_name.c_str()), | |
| 202 subject_name_size))) { | |
| 203 return base::string16(); | |
| 204 } | |
| 205 | |
| 206 return subject_name; | |
| 207 } | |
| 208 | |
| 209 // Helper for scoped tracking a catalog admin context. | |
| 210 struct CryptCATContextScopedTraits { | |
| 211 static PVOID InvalidValue() { return nullptr; } | |
| 212 static void Free(PVOID context) { | |
| 213 CryptCATAdminReleaseContext(context, 0); | |
| 214 } | |
| 215 }; | |
| 216 using ScopedCryptCATContext = | |
| 217 base::ScopedGeneric<PVOID, CryptCATContextScopedTraits>; | |
| 218 | |
| 219 // Helper for scoped tracking of a catalog context. A catalog context is only | |
| 220 // valid with an associated admin context, so this is effectively a std::pair. | |
| 221 // A custom operator!= is required in order for a null |catalog_context| but | |
| 222 // non-null |context| to compare equal to the InvalidValue exposed by the | |
| 223 // traits class. | |
| 224 class CryptCATCatalogContext { | |
| 225 public: | |
| 226 CryptCATCatalogContext(PVOID context, PVOID catalog_context) | |
| 227 : context_(context), catalog_context_(catalog_context) {} | |
| 228 | |
| 229 bool operator!=(const CryptCATCatalogContext& rhs) const { | |
| 230 return catalog_context_ != rhs.catalog_context_; | |
| 231 } | |
| 232 | |
| 233 PVOID context() const { return context_; } | |
| 234 PVOID catalog_context() const { return catalog_context_; } | |
| 235 | |
| 236 private: | |
| 237 PVOID context_; | |
| 238 PVOID catalog_context_; | |
| 239 }; | |
| 240 | |
| 241 struct CryptCATCatalogContextScopedTraits { | |
| 242 static CryptCATCatalogContext InvalidValue() { | |
| 243 return CryptCATCatalogContext(nullptr, nullptr); | |
| 244 } | |
| 245 static void Free(const CryptCATCatalogContext& c) { | |
| 246 CryptCATAdminReleaseCatalogContext( | |
| 247 c.context(), c.catalog_context(), 0); | |
| 248 } | |
| 249 }; | |
| 250 using ScopedCryptCATCatalogContext = base::ScopedGeneric< | |
| 251 CryptCATCatalogContext, CryptCATCatalogContextScopedTraits>; | |
| 252 | |
| 253 // Extracts the subject name and catalog path if the provided file is present in | |
| 254 // a catalog file. | |
| 255 void GetCatalogCertificateInfo(const base::FilePath& filename, | |
| 256 ModuleEnumerator::CertificateInfo* cert_info) { | |
| 257 // Get a crypt context for signature verification. | |
| 258 ScopedCryptCATContext context; | |
| 259 { | |
| 260 PVOID raw_context = nullptr; | |
| 261 if (!CryptCATAdminAcquireContext(&raw_context, nullptr, 0)) | |
| 262 return; | |
| 263 context.reset(raw_context); | |
| 264 } | |
| 265 | |
| 266 // Open the file of interest. | |
| 267 base::win::ScopedHandle file_handle(CreateFileW( | |
| 268 filename.value().c_str(), GENERIC_READ, | |
| 269 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 270 nullptr, OPEN_EXISTING, 0, nullptr)); | |
| 271 if (!file_handle.IsValid()) | |
| 272 return; | |
| 273 | |
| 274 // Get the size we need for our hash. | |
| 275 DWORD hash_size = 0; | |
| 276 CryptCATAdminCalcHashFromFileHandle( | |
| 277 file_handle.Get(), &hash_size, nullptr, 0); | |
| 278 if (hash_size == 0) | |
| 279 return; | |
| 280 | |
| 281 // Calculate the hash. If this fails then bail. | |
| 282 std::vector<BYTE> buffer(hash_size); | |
| 283 if (!CryptCATAdminCalcHashFromFileHandle(file_handle.Get(), &hash_size, | |
| 284 buffer.data(), 0)) { | |
| 285 return; | |
| 286 } | |
| 287 | |
| 288 // Get catalog for our context. | |
| 289 ScopedCryptCATCatalogContext catalog_context(CryptCATCatalogContext( | |
| 290 context.get(), | |
| 291 CryptCATAdminEnumCatalogFromHash(context.get(), buffer.data(), hash_size, | |
| 292 0, nullptr))); | |
| 293 if (!catalog_context.is_valid()) | |
| 294 return; | |
| 295 | |
| 296 // Get the catalog info. This includes the path to the catalog itself, which | |
| 297 // contains the signature of interest. | |
| 298 CATALOG_INFO catalog_info = {}; | |
| 299 catalog_info.cbStruct = sizeof(catalog_info); | |
| 300 if (!CryptCATCatalogInfoFromContext( | |
| 301 catalog_context.get().catalog_context(), &catalog_info, 0)) { | |
| 302 return; | |
| 303 } | |
| 304 | |
| 305 // Attempt to get the "Subject" field from the signature of the catalog file | |
| 306 // itself. | |
| 307 base::FilePath catalog_path(catalog_info.wszCatalogFile); | |
| 308 base::string16 subject = GetSubjectNameInFile(catalog_path); | |
| 309 | |
| 310 if (subject.empty()) | |
| 311 return; | |
| 312 | |
| 313 cert_info->type = ModuleEnumerator::CERTIFICATE_IN_CATALOG; | |
| 314 cert_info->path = catalog_path; | |
| 315 cert_info->subject = subject; | |
| 316 } | |
| 317 | |
| 318 // Extracts information about the certificate of the given file, if any is | |
| 319 // found. | |
| 320 void GetCertificateInfo(const base::FilePath& filename, | |
| 321 ModuleEnumerator::CertificateInfo* cert_info) { | |
| 322 DCHECK_EQ(ModuleEnumerator::NO_CERTIFICATE, cert_info->type); | |
| 323 DCHECK(cert_info->path.empty()); | |
| 324 DCHECK(cert_info->subject.empty()); | |
| 325 | |
| 326 GetCatalogCertificateInfo(filename, cert_info); | |
| 327 if (cert_info->type == ModuleEnumerator::CERTIFICATE_IN_CATALOG) | |
| 328 return; | |
| 329 | |
| 330 base::string16 subject = GetSubjectNameInFile(filename); | |
| 331 if (subject.empty()) | |
| 332 return; | |
| 333 | |
| 334 cert_info->type = ModuleEnumerator::CERTIFICATE_IN_FILE; | |
| 335 cert_info->path = filename; | |
| 336 cert_info->subject = subject; | |
| 337 } | |
| 338 | |
| 339 } // namespace | 100 } // namespace |
| 340 | 101 |
| 341 ModuleEnumerator::CertificateInfo::CertificateInfo() : type(NO_CERTIFICATE) {} | |
| 342 | |
| 343 ModuleEnumerator::Module::Module() { | 102 ModuleEnumerator::Module::Module() { |
| 344 } | 103 } |
| 345 | 104 |
| 346 ModuleEnumerator::Module::Module(const Module& rhs) = default; | 105 ModuleEnumerator::Module::Module(const Module& rhs) = default; |
| 347 | 106 |
| 348 ModuleEnumerator::Module::Module(ModuleType type, | 107 ModuleEnumerator::Module::Module(ModuleType type, |
| 349 ModuleStatus status, | 108 ModuleStatus status, |
| 350 const base::string16& location, | 109 const base::string16& location, |
| 351 const base::string16& name, | 110 const base::string16& name, |
| 352 const base::string16& product_name, | 111 const base::string16& product_name, |
| (...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 702 // catalog counts as a single certificate, as does a file with a baked in | 461 // catalog counts as a single certificate, as does a file with a baked in |
| 703 // certificate. | 462 // certificate. |
| 704 std::set<base::FilePath> unique_certificates; | 463 std::set<base::FilePath> unique_certificates; |
| 705 size_t microsoft_certificates = 0; | 464 size_t microsoft_certificates = 0; |
| 706 size_t signed_modules = 0; | 465 size_t signed_modules = 0; |
| 707 size_t microsoft_modules = 0; | 466 size_t microsoft_modules = 0; |
| 708 size_t catalog_modules = 0; | 467 size_t catalog_modules = 0; |
| 709 size_t third_party_loaded = 0; | 468 size_t third_party_loaded = 0; |
| 710 size_t third_party_not_loaded = 0; | 469 size_t third_party_not_loaded = 0; |
| 711 for (const auto& module : *enumerated_modules_) { | 470 for (const auto& module : *enumerated_modules_) { |
| 712 if (module.cert_info.type != ModuleEnumerator::NO_CERTIFICATE) { | 471 if (module.cert_info.type != ModuleDatabase::NO_CERTIFICATE) { |
| 713 ++signed_modules; | 472 ++signed_modules; |
| 714 | 473 |
| 715 if (module.cert_info.type == ModuleEnumerator::CERTIFICATE_IN_CATALOG) | 474 if (module.cert_info.type == ModuleDatabase::CERTIFICATE_IN_CATALOG) |
| 716 ++catalog_modules; | 475 ++catalog_modules; |
| 717 | 476 |
| 718 // The first time this certificate is encountered it will be inserted | 477 // The first time this certificate is encountered it will be inserted |
| 719 // into the set. | 478 // into the set. |
| 720 bool new_certificate = | 479 bool new_certificate = |
| 721 unique_certificates.insert(module.cert_info.path).second; | 480 unique_certificates.insert(module.cert_info.path).second; |
| 722 | 481 |
| 723 // Check if the signer name begins with "Microsoft ". Signatures are | 482 // Check if the signer name begins with "Microsoft ". Signatures are |
| 724 // typically "Microsoft Corporation" or "Microsoft Windows", but others | 483 // typically "Microsoft Corporation" or "Microsoft Windows", but others |
| 725 // may exist. | 484 // may exist. |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1011 | 770 |
| 1012 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", | 771 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", |
| 1013 suspected_bad_modules_detected_); | 772 suspected_bad_modules_detected_); |
| 1014 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", | 773 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", |
| 1015 confirmed_bad_modules_detected_); | 774 confirmed_bad_modules_detected_); |
| 1016 | 775 |
| 1017 // Forward the callback to any registered observers. | 776 // Forward the callback to any registered observers. |
| 1018 for (Observer& observer : observers_) | 777 for (Observer& observer : observers_) |
| 1019 observer.OnScanCompleted(); | 778 observer.OnScanCompleted(); |
| 1020 } | 779 } |
| OLD | NEW |