1 // Copyright 2015 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.
5 #include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
7 #include "base/base_switches.h"
8 #include "base/command_line.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_util.h"
12 #include "base/version.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/extensions/extension_constants.h"
16 #include "extensions/browser/extension_prefs.h"
17 #include "extensions/browser/extension_registry.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/browser/management_policy.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/extension_urls.h"
22 #include "extensions/common/extensions_client.h"
23 #include "extensions/common/manifest.h"
24 #include "extensions/common/manifest_url_handlers.h"
25 #include "net/base/escape.h"
27 #if defined(OS_CHROMEOS)
28 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
33 const char kContentVerificationExperimentName
[] =
34 "ExtensionContentVerification";
38 namespace extensions
{
41 ContentVerifierDelegate::Mode
ChromeContentVerifierDelegate::GetDefaultMode() {
42 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
44 Mode experiment_value
;
45 #if defined(GOOGLE_CHROME_BUILD)
46 experiment_value
= ContentVerifierDelegate::ENFORCE
;
48 experiment_value
= ContentVerifierDelegate::NONE
;
50 const std::string group
=
51 base::FieldTrialList::FindFullName(kContentVerificationExperimentName
);
52 if (group
== "EnforceStrict")
53 experiment_value
= ContentVerifierDelegate::ENFORCE_STRICT
;
54 else if (group
== "Enforce")
55 experiment_value
= ContentVerifierDelegate::ENFORCE
;
56 else if (group
== "Bootstrap")
57 experiment_value
= ContentVerifierDelegate::BOOTSTRAP
;
58 else if (group
== "None")
59 experiment_value
= ContentVerifierDelegate::NONE
;
61 // The field trial value that normally comes from the server can be
62 // overridden on the command line, which we don't want to allow since
63 // malware can set chrome command line flags. There isn't currently a way
64 // to find out what the server-provided value is in this case, so we
65 // conservatively default to the strictest mode if we detect our experiment
66 // name being overridden.
67 if (command_line
->HasSwitch(switches::kForceFieldTrials
)) {
68 std::string forced_trials
=
69 command_line
->GetSwitchValueASCII(switches::kForceFieldTrials
);
70 if (forced_trials
.find(kContentVerificationExperimentName
) !=
72 experiment_value
= ContentVerifierDelegate::ENFORCE_STRICT
;
75 Mode cmdline_value
= NONE
;
76 if (command_line
->HasSwitch(switches::kExtensionContentVerification
)) {
77 std::string switch_value
= command_line
->GetSwitchValueASCII(
78 switches::kExtensionContentVerification
);
79 if (switch_value
== switches::kExtensionContentVerificationBootstrap
)
80 cmdline_value
= ContentVerifierDelegate::BOOTSTRAP
;
81 else if (switch_value
== switches::kExtensionContentVerificationEnforce
)
82 cmdline_value
= ContentVerifierDelegate::ENFORCE
;
83 else if (switch_value
==
84 switches::kExtensionContentVerificationEnforceStrict
)
85 cmdline_value
= ContentVerifierDelegate::ENFORCE_STRICT
;
87 // If no value was provided (or the wrong one), just default to enforce.
88 cmdline_value
= ContentVerifierDelegate::ENFORCE
;
91 // We don't want to allow the command-line flags to eg disable enforcement
92 // if the experiment group says it should be on, or malware may just modify
93 // the command line flags. So return the more restrictive of the 2 values.
94 return std::max(experiment_value
, cmdline_value
);
97 ChromeContentVerifierDelegate::ChromeContentVerifierDelegate(
98 content::BrowserContext
* context
)
99 : context_(context
), default_mode_(GetDefaultMode()) {
102 ChromeContentVerifierDelegate::~ChromeContentVerifierDelegate() {
105 ContentVerifierDelegate::Mode
ChromeContentVerifierDelegate::ShouldBeVerified(
106 const Extension
& extension
) {
107 #if defined(OS_CHROMEOS)
108 if (ExtensionAssetsManagerChromeOS::IsSharedInstall(&extension
))
109 return ContentVerifierDelegate::ENFORCE_STRICT
;
112 if (!extension
.is_extension() && !extension
.is_legacy_packaged_app())
113 return ContentVerifierDelegate::NONE
;
114 if (!Manifest::IsAutoUpdateableLocation(extension
.location()))
115 return ContentVerifierDelegate::NONE
;
117 if (!ManifestURL::UpdatesFromGallery(&extension
)) {
118 // It's possible that the webstore update url was overridden for testing
119 // so also consider extensions with the default (production) update url
120 // to be from the store as well.
121 GURL default_webstore_url
= extension_urls::GetDefaultWebstoreUpdateUrl();
122 if (ManifestURL::GetUpdateURL(&extension
) != default_webstore_url
)
123 return ContentVerifierDelegate::NONE
;
126 return default_mode_
;
129 ContentVerifierKey
ChromeContentVerifierDelegate::GetPublicKey() {
130 return ContentVerifierKey(extension_misc::kWebstoreSignaturesPublicKey
,
131 extension_misc::kWebstoreSignaturesPublicKeySize
);
134 GURL
ChromeContentVerifierDelegate::GetSignatureFetchUrl(
135 const std::string
& extension_id
,
136 const base::Version
& version
) {
137 // TODO(asargent) Factor out common code from the extension updater's
138 // ManifestFetchData class that can be shared for use here.
139 std::vector
<std::string
> parts
;
140 parts
.push_back("uc");
141 parts
.push_back("installsource=signature");
142 parts
.push_back("id=" + extension_id
);
143 parts
.push_back("v=" + version
.GetString());
144 std::string x_value
=
145 net::EscapeQueryParamValue(base::JoinString(parts
, "&"), true);
146 std::string query
= "response=redirect&x=" + x_value
;
148 GURL base_url
= extension_urls::GetWebstoreUpdateUrl();
149 GURL::Replacements replacements
;
150 replacements
.SetQuery(query
.c_str(), url::Component(0, query
.length()));
151 return base_url
.ReplaceComponents(replacements
);
154 std::set
<base::FilePath
> ChromeContentVerifierDelegate::GetBrowserImagePaths(
155 const extensions::Extension
* extension
) {
156 return ExtensionsClient::Get()->GetBrowserImagePaths(extension
);
159 void ChromeContentVerifierDelegate::VerifyFailed(
160 const std::string
& extension_id
,
161 ContentVerifyJob::FailureReason reason
) {
162 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context_
);
163 const Extension
* extension
=
164 registry
->enabled_extensions().GetByID(extension_id
);
167 ExtensionSystem
* system
= ExtensionSystem::Get(context_
);
168 Mode mode
= ShouldBeVerified(*extension
);
169 if (mode
>= ContentVerifierDelegate::ENFORCE
) {
170 if (!system
->management_policy()->UserMayModifySettings(extension
, NULL
)) {
171 LogFailureForPolicyForceInstall(extension_id
);
174 system
->extension_service()->DisableExtension(extension_id
,
175 Extension::DISABLE_CORRUPTED
);
176 ExtensionPrefs::Get(context_
)->IncrementCorruptedDisableCount();
177 UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptExtensionBecameDisabled", true);
178 UMA_HISTOGRAM_ENUMERATION("Extensions.CorruptExtensionDisabledReason",
179 reason
, ContentVerifyJob::FAILURE_REASON_MAX
);
180 } else if (!ContainsKey(would_be_disabled_ids_
, extension_id
)) {
181 UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptExtensionWouldBeDisabled", true);
182 would_be_disabled_ids_
.insert(extension_id
);
186 void ChromeContentVerifierDelegate::LogFailureForPolicyForceInstall(
187 const std::string
& extension_id
) {
188 if (!ContainsKey(corrupt_policy_extensions_
, extension_id
)) {
189 corrupt_policy_extensions_
.insert(extension_id
);
190 UMA_HISTOGRAM_BOOLEAN("Extensions.CorruptPolicyExtensionWouldBeDisabled",
195 } // namespace extensions