| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/conflicts/third_party_conflicts_manager_win.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/base_paths.h" |
| #include "base/bind.h" |
| #include "base/feature_list.h" |
| #include "base/location.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/path_service.h" |
| #include "base/task_scheduler/post_task.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/conflicts/incompatible_applications_updater_win.h" |
| #include "chrome/browser/conflicts/installed_applications_win.h" |
| #include "chrome/browser/conflicts/module_blacklist_cache_updater_win.h" |
| #include "chrome/browser/conflicts/module_database_win.h" |
| #include "chrome/browser/conflicts/module_info_util_win.h" |
| #include "chrome/browser/conflicts/module_list_filter_win.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| namespace { |
| |
| std::unique_ptr<CertificateInfo> CreateExeCertificateInfo() { |
| auto certificate_info = std::make_unique<CertificateInfo>(); |
| |
| base::FilePath exe_path; |
| if (base::PathService::Get(base::FILE_EXE, &exe_path)) |
| GetCertificateInfo(exe_path, certificate_info.get()); |
| |
| return certificate_info; |
| } |
| |
| std::unique_ptr<ModuleListFilter> CreateModuleListFilter( |
| const base::FilePath& module_list_path) { |
| auto module_list_filter = std::make_unique<ModuleListFilter>(); |
| |
| if (!module_list_filter->Initialize(module_list_path)) |
| return nullptr; |
| |
| return module_list_filter; |
| } |
| |
| } // namespace |
| |
| ThirdPartyConflictsManager::ThirdPartyConflictsManager( |
| ModuleDatabase* module_database) |
| : module_database_(module_database), |
| module_list_received_(false), |
| on_module_database_idle_called_(false), |
| weak_ptr_factory_(this) { |
| base::PostTaskWithTraitsAndReplyWithResult( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BACKGROUND, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce(&CreateExeCertificateInfo), |
| base::BindOnce(&ThirdPartyConflictsManager::OnExeCertificateCreated, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| ThirdPartyConflictsManager::~ThirdPartyConflictsManager() = default; |
| |
| // static |
| void ThirdPartyConflictsManager::RegisterLocalStatePrefs( |
| PrefRegistrySimple* registry) { |
| // Register the pref used to disable the Incompatible Applications warning and |
| // the blocking of third-party modules using group policy. Enabled by default. |
| registry->RegisterBooleanPref(prefs::kThirdPartyBlockingEnabled, true); |
| |
| // Register the pref that remembers the MD5 digest for the current module |
| // blacklist cache. The default value is an invalid MD5 digest. |
| registry->RegisterStringPref(prefs::kModuleBlacklistCacheMD5Digest, ""); |
| } |
| |
| // static |
| bool ThirdPartyConflictsManager::IsThirdPartyBlockingPolicyEnabled() { |
| const PrefService::Preference* third_party_blocking_enabled_pref = |
| g_browser_process->local_state()->FindPreference( |
| prefs::kThirdPartyBlockingEnabled); |
| return !third_party_blocking_enabled_pref->IsManaged() || |
| third_party_blocking_enabled_pref->GetValue()->GetBool(); |
| } |
| |
| // static |
| void ThirdPartyConflictsManager::DisableThirdPartyModuleBlocking() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // Delete the module blacklist cache. Since the NtMapViewOfSection hook only |
| // blocks if the file is present, this will deactivate third-party modules |
| // blocking for the next browser launch. |
| base::PostTaskWithTraits( |
| FROM_HERE, |
| {base::TaskPriority::BACKGROUND, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock()}, |
| base::BindOnce(&ModuleBlacklistCacheUpdater::DeleteModuleBlacklistCache)); |
| |
| // Also clear the MD5 digest since there will no longer be a current module |
| // blacklist cache. |
| g_browser_process->local_state()->ClearPref( |
| prefs::kModuleBlacklistCacheMD5Digest); |
| } |
| |
| void ThirdPartyConflictsManager::OnModuleDatabaseIdle() { |
| if (on_module_database_idle_called_) |
| return; |
| |
| on_module_database_idle_called_ = true; |
| |
| base::PostTaskWithTraitsAndReplyWithResult( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BACKGROUND, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce( |
| []() { return std::make_unique<InstalledApplications>(); }), |
| base::BindOnce( |
| &ThirdPartyConflictsManager::OnInstalledApplicationsCreated, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ThirdPartyConflictsManager::LoadModuleList(const base::FilePath& path) { |
| if (module_list_received_) |
| return; |
| |
| module_list_received_ = true; |
| |
| base::PostTaskWithTraitsAndReplyWithResult( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BACKGROUND, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce(&CreateModuleListFilter, path), |
| base::BindOnce(&ThirdPartyConflictsManager::OnModuleListFilterCreated, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ThirdPartyConflictsManager::OnExeCertificateCreated( |
| std::unique_ptr<CertificateInfo> exe_certificate_info) { |
| exe_certificate_info_ = std::move(exe_certificate_info); |
| |
| if (module_list_filter_ && installed_applications_) |
| InitializeIncompatibleApplicationsUpdater(); |
| |
| if (module_list_filter_) |
| MaybeInitializeModuleBlacklistCacheUpdater(); |
| } |
| |
| void ThirdPartyConflictsManager::OnModuleListFilterCreated( |
| std::unique_ptr<ModuleListFilter> module_list_filter) { |
| module_list_filter_ = std::move(module_list_filter); |
| |
| // A valid |module_list_filter_| is critical to the blocking of third-party |
| // modules. By returning early here, the |incompatible_applications_updater_| |
| // instance never gets created, thus disabling the identification of |
| // incompatible applications. |
| if (!module_list_filter_) { |
| // Mark the module list as not received so that a new one may trigger the |
| // creation of a valid filter. |
| module_list_received_ = false; |
| return; |
| } |
| |
| if (exe_certificate_info_ && installed_applications_) |
| InitializeIncompatibleApplicationsUpdater(); |
| |
| if (exe_certificate_info_) |
| MaybeInitializeModuleBlacklistCacheUpdater(); |
| } |
| |
| void ThirdPartyConflictsManager::OnInstalledApplicationsCreated( |
| std::unique_ptr<InstalledApplications> installed_applications) { |
| installed_applications_ = std::move(installed_applications); |
| |
| if (exe_certificate_info_ && module_list_filter_) |
| InitializeIncompatibleApplicationsUpdater(); |
| } |
| |
| void ThirdPartyConflictsManager::InitializeIncompatibleApplicationsUpdater() { |
| DCHECK(exe_certificate_info_); |
| DCHECK(module_list_filter_); |
| DCHECK(installed_applications_); |
| |
| incompatible_applications_updater_ = |
| std::make_unique<IncompatibleApplicationsUpdater>( |
| *exe_certificate_info_, *module_list_filter_, |
| *installed_applications_); |
| module_database_->AddObserver(incompatible_applications_updater_.get()); |
| } |
| |
| void ThirdPartyConflictsManager::MaybeInitializeModuleBlacklistCacheUpdater() { |
| DCHECK(exe_certificate_info_); |
| DCHECK(module_list_filter_); |
| |
| if (!base::FeatureList::IsEnabled(features::kThirdPartyModulesBlocking)) |
| return; |
| |
| // Create the instance. It is safe to use base::Unretained() since the |
| // callback is not invoked when the updater is freed. |
| module_blacklist_cache_updater_ = |
| std::make_unique<ModuleBlacklistCacheUpdater>( |
| *exe_certificate_info_, *module_list_filter_, |
| base::BindRepeating( |
| &ThirdPartyConflictsManager::OnModuleBlacklistCacheUpdated, |
| base::Unretained(this))); |
| module_database_->AddObserver(module_blacklist_cache_updater_.get()); |
| } |
| |
| void ThirdPartyConflictsManager::OnModuleBlacklistCacheUpdated( |
| const ModuleBlacklistCacheUpdater::CacheUpdateResult& result) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // Check that the MD5 digest of the old cache matches what was expected. Only |
| // used for reporting a metric. |
| const PrefService::Preference* preference = |
| g_browser_process->local_state()->FindPreference( |
| prefs::kModuleBlacklistCacheMD5Digest); |
| DCHECK(preference); |
| |
| // The first time this is executed, the pref doesn't yet hold a valid MD5 |
| // digest. |
| if (!preference->IsDefaultValue()) { |
| const std::string old_md5_string = |
| base::MD5DigestToBase16(result.old_md5_digest); |
| const std::string& current_md5_string = preference->GetValue()->GetString(); |
| UMA_HISTOGRAM_BOOLEAN("ModuleBlacklistCache.ExpectedMD5Digest", |
| old_md5_string == current_md5_string); |
| } |
| |
| // Set the expected MD5 digest for the next time the cache is updated. |
| g_browser_process->local_state()->Set( |
| prefs::kModuleBlacklistCacheMD5Digest, |
| base::Value(base::MD5DigestToBase16(result.new_md5_digest))); |
| } |