| // Copyright 2015 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/extensions/chrome_content_verifier_delegate.h" |
| |
| #include "base/base_switches.h" |
| #include "base/command_line.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram.h" |
| #include "base/strings/string_util.h" |
| #include "base/version.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/management_policy.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_urls.h" |
| #include "extensions/common/extensions_client.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_url_handlers.h" |
| #include "net/base/escape.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/extensions/extension_assets_manager_chromeos.h" |
| #endif |
| |
| namespace { |
| |
| const char kContentVerificationExperimentName[] = |
| "ExtensionContentVerification"; |
| |
| } // namespace |
| |
| namespace extensions { |
| |
| // static |
| ContentVerifierDelegate::Mode ChromeContentVerifierDelegate::GetDefaultMode() { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| |
| Mode experiment_value; |
| #if defined(GOOGLE_CHROME_BUILD) |
| experiment_value = ContentVerifierDelegate::ENFORCE; |
| #else |
| experiment_value = ContentVerifierDelegate::NONE; |
| #endif |
| const std::string group = |
| base::FieldTrialList::FindFullName(kContentVerificationExperimentName); |
| if (group == "EnforceStrict") |
| experiment_value = ContentVerifierDelegate::ENFORCE_STRICT; |
| else if (group == "Enforce") |
| experiment_value = ContentVerifierDelegate::ENFORCE; |
| else if (group == "Bootstrap") |
| experiment_value = ContentVerifierDelegate::BOOTSTRAP; |
| else if (group == "None") |
| experiment_value = ContentVerifierDelegate::NONE; |
| |
| // The field trial value that normally comes from the server can be |
| // overridden on the command line, which we don't want to allow since |
| // malware can set chrome command line flags. There isn't currently a way |
| // to find out what the server-provided value is in this case, so we |
| // conservatively default to the strictest mode if we detect our experiment |
| // name being overridden. |
| if (command_line->HasSwitch(switches::kForceFieldTrials)) { |
| std::string forced_trials = |
| command_line->GetSwitchValueASCII(switches::kForceFieldTrials); |
| if (forced_trials.find(kContentVerificationExperimentName) != |
| std::string::npos) |
| experiment_value = ContentVerifierDelegate::ENFORCE_STRICT; |
| } |
| |
| Mode cmdline_value = NONE; |
| if (command_line->HasSwitch(switches::kExtensionContentVerification)) { |
| std::string switch_value = command_line->GetSwitchValueASCII( |
| switches::kExtensionContentVerification); |
| if (switch_value == switches::kExtensionContentVerificationBootstrap) |
| cmdline_value = ContentVerifierDelegate::BOOTSTRAP; |
| else if (switch_value == switches::kExtensionContentVerificationEnforce) |
| cmdline_value = ContentVerifierDelegate::ENFORCE; |
| else if (switch_value == |
| switches::kExtensionContentVerificationEnforceStrict) |
| cmdline_value = ContentVerifierDelegate::ENFORCE_STRICT; |
| else |
| // If no value was provided (or the wrong one), just default to enforce. |
| cmdline_value = ContentVerifierDelegate::ENFORCE; |
| } |
| |
| // We don't want to allow the command-line flags to eg disable enforcement |
| // if the experiment group says it should be on, or malware may just modify |
| // the command line flags. So return the more restrictive of the 2 values. |
| return std::max(experiment_value, cmdline_value); |
| } |
| |
| ChromeContentVerifierDelegate::ChromeContentVerifierDelegate( |
| content::BrowserContext* context) |
| : context_(context), default_mode_(GetDefaultMode()) { |
| } |
| |
| ChromeContentVerifierDelegate::~ChromeContentVerifierDelegate() { |
| } |
| |
| ContentVerifierDelegate::Mode ChromeContentVerifierDelegate::ShouldBeVerified( |
| const Extension& extension) { |
| #if defined(OS_CHROMEOS) |
| if (ExtensionAssetsManagerChromeOS::IsSharedInstall(&extension)) |
| return ContentVerifierDelegate::ENFORCE_STRICT; |
| #endif |
| |
| if (!extension.is_extension() && !extension.is_legacy_packaged_app()) |
| return ContentVerifierDelegate::NONE; |
| if (!Manifest::IsAutoUpdateableLocation(extension.location())) |
| return ContentVerifierDelegate::NONE; |
| |
| if (!ManifestURL::UpdatesFromGallery(&extension)) { |
| // It's possible that the webstore update url was overridden for testing |
| // so also consider extensions with the default (production) update url |
| // to be from the store as well. |
| GURL default_webstore_url = extension_urls::GetDefaultWebstoreUpdateUrl(); |
| if (ManifestURL::GetUpdateURL(&extension) != default_webstore_url) |
| return ContentVerifierDelegate::NONE; |
| } |
| |
| return default_mode_; |
| } |
| |
| ContentVerifierKey ChromeContentVerifierDelegate::GetPublicKey() { |
| return ContentVerifierKey(extension_misc::kWebstoreSignaturesPublicKey, |
| extension_misc::kWebstoreSignaturesPublicKeySize); |
| } |
| |
| GURL ChromeContentVerifierDelegate::GetSignatureFetchUrl( |
| const std::string& extension_id, |
| const base::Version& version) { |
| // TODO(asargent) Factor out common code from the extension updater's |
| // ManifestFetchData class that can be shared for use here. |
| std::vector<std::string> parts; |
| parts.push_back("uc"); |
| parts.push_back("installsource=signature"); |
| parts.push_back("id=" + extension_id); |
| parts.push_back("v=" + version.GetString()); |
| std::string x_value = |
| net::EscapeQueryParamValue(base::JoinString(parts, "&"), true); |
| std::string query = "response=redirect&x=" + x_value; |
| |
| GURL base_url = extension_urls::GetWebstoreUpdateUrl(); |
| GURL::Replacements replacements; |
| replacements.SetQuery(query.c_str(), url::Component(0, query.length())); |
| return base_url.ReplaceComponents(replacements); |
| } |
| |
| std::set<base::FilePath> ChromeContentVerifierDelegate::GetBrowserImagePaths( |
| const extensions::Extension* extension) { |
| return ExtensionsClient::Get()->GetBrowserImagePaths(extension); |
| } |
| |
| void ChromeContentVerifierDelegate::VerifyFailed( |
| const std::string& extension_id, |
| ContentVerifyJob::FailureReason reason) { |
| ExtensionRegistry* registry = ExtensionRegistry::Get(context_); |
| const Extension* extension = |
| registry->enabled_extensions().GetByID(extension_id); |
| if (!extension) |
| return; |
| ExtensionSystem* system = ExtensionSystem::Get(context_); |
| Mode mode = ShouldBeVerified(*extension); |
| if (mode >= ContentVerifierDelegate::ENFORCE) { |
| if (!system->management_policy()->UserMayModifySettings(extension, NULL)) { |
| LogFailureForPolicyForceInstall(extension_id); |
| return; |
| } |
| system->extension_service()->DisableExtension(extension_id, |
| Extension::DISABLE_CORRUPTED); |
| ExtensionPrefs::Get(context_)->IncrementCorruptedDisableCount(); |
| UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptExtensionBecameDisabled", true); |
| UMA_HISTOGRAM_ENUMERATION("Extensions.CorruptExtensionDisabledReason", |
| reason, ContentVerifyJob::FAILURE_REASON_MAX); |
| } else if (!ContainsKey(would_be_disabled_ids_, extension_id)) { |
| UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptExtensionWouldBeDisabled", true); |
| would_be_disabled_ids_.insert(extension_id); |
| } |
| } |
| |
| void ChromeContentVerifierDelegate::LogFailureForPolicyForceInstall( |
| const std::string& extension_id) { |
| if (!ContainsKey(corrupt_policy_extensions_, extension_id)) { |
| corrupt_policy_extensions_.insert(extension_id); |
| UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptPolicyExtensionWouldBeDisabled", |
| true); |
| } |
| } |
| |
| } // namespace extensions |