// 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 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 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