1 // Copyright 2014 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/extension_management.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_util.h"
16 #include "base/trace_event/trace_event.h"
17 #include "base/version.h"
18 #include "chrome/browser/extensions/extension_management_constants.h"
19 #include "chrome/browser/extensions/extension_management_internal.h"
20 #include "chrome/browser/extensions/external_policy_loader.h"
21 #include "chrome/browser/extensions/external_provider_impl.h"
22 #include "chrome/browser/extensions/permissions_based_management_policy_provider.h"
23 #include "chrome/browser/extensions/standard_management_policy_provider.h"
24 #include "chrome/browser/profiles/incognito_helpers.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "components/crx_file/id_util.h"
27 #include "components/keyed_service/content/browser_context_dependency_manager.h"
28 #include "components/pref_registry/pref_registry_syncable.h"
29 #include "extensions/browser/pref_names.h"
30 #include "extensions/common/manifest_constants.h"
31 #include "extensions/common/permissions/api_permission_set.h"
32 #include "extensions/common/permissions/permission_set.h"
33 #include "extensions/common/url_pattern.h"
36 namespace extensions
{
38 ExtensionManagement::ExtensionManagement(PrefService
* pref_service
)
39 : pref_service_(pref_service
) {
40 TRACE_EVENT0("browser,startup",
41 "ExtensionManagement::ExtensionManagement::ctor");
42 pref_change_registrar_
.Init(pref_service_
);
43 base::Closure pref_change_callback
= base::Bind(
44 &ExtensionManagement::OnExtensionPrefChanged
, base::Unretained(this));
45 pref_change_registrar_
.Add(pref_names::kInstallAllowList
,
46 pref_change_callback
);
47 pref_change_registrar_
.Add(pref_names::kInstallDenyList
,
48 pref_change_callback
);
49 pref_change_registrar_
.Add(pref_names::kInstallForceList
,
50 pref_change_callback
);
51 pref_change_registrar_
.Add(pref_names::kAllowedInstallSites
,
52 pref_change_callback
);
53 pref_change_registrar_
.Add(pref_names::kAllowedTypes
, pref_change_callback
);
54 pref_change_registrar_
.Add(pref_names::kExtensionManagement
,
55 pref_change_callback
);
56 // Note that both |global_settings_| and |default_settings_| will be null
57 // before first call to Refresh(), so in order to resolve this, Refresh() must
58 // be called in the initialization of ExtensionManagement.
60 providers_
.push_back(new StandardManagementPolicyProvider(this));
61 providers_
.push_back(new PermissionsBasedManagementPolicyProvider(this));
64 ExtensionManagement::~ExtensionManagement() {
67 void ExtensionManagement::Shutdown() {
68 pref_change_registrar_
.RemoveAll();
69 pref_service_
= nullptr;
72 void ExtensionManagement::AddObserver(Observer
* observer
) {
73 observer_list_
.AddObserver(observer
);
76 void ExtensionManagement::RemoveObserver(Observer
* observer
) {
77 observer_list_
.RemoveObserver(observer
);
80 std::vector
<ManagementPolicy::Provider
*> ExtensionManagement::GetProviders()
82 return providers_
.get();
85 bool ExtensionManagement::BlacklistedByDefault() const {
86 return default_settings_
->installation_mode
== INSTALLATION_BLOCKED
;
89 ExtensionManagement::InstallationMode
ExtensionManagement::GetInstallationMode(
90 const Extension
* extension
) const {
91 // Check per-extension installation mode setting first.
92 auto iter_id
= settings_by_id_
.find(extension
->id());
93 if (iter_id
!= settings_by_id_
.end())
94 return iter_id
->second
->installation_mode
;
95 std::string update_url
;
96 // Check per-update-url installation mode setting.
97 if (extension
->manifest()->GetString(manifest_keys::kUpdateURL
,
99 auto iter_update_url
= settings_by_update_url_
.find(update_url
);
100 if (iter_update_url
!= settings_by_update_url_
.end())
101 return iter_update_url
->second
->installation_mode
;
103 // Fall back to default installation mode setting.
104 return default_settings_
->installation_mode
;
107 scoped_ptr
<base::DictionaryValue
> ExtensionManagement::GetForceInstallList()
109 scoped_ptr
<base::DictionaryValue
> install_list(new base::DictionaryValue());
110 for (SettingsIdMap::const_iterator it
= settings_by_id_
.begin();
111 it
!= settings_by_id_
.end();
113 if (it
->second
->installation_mode
== INSTALLATION_FORCED
) {
114 ExternalPolicyLoader::AddExtension(
115 install_list
.get(), it
->first
, it
->second
->update_url
);
118 return install_list
.Pass();
121 scoped_ptr
<base::DictionaryValue
>
122 ExtensionManagement::GetRecommendedInstallList() const {
123 scoped_ptr
<base::DictionaryValue
> install_list(new base::DictionaryValue());
124 for (SettingsIdMap::const_iterator it
= settings_by_id_
.begin();
125 it
!= settings_by_id_
.end();
127 if (it
->second
->installation_mode
== INSTALLATION_RECOMMENDED
) {
128 ExternalPolicyLoader::AddExtension(
129 install_list
.get(), it
->first
, it
->second
->update_url
);
132 return install_list
.Pass();
135 bool ExtensionManagement::IsInstallationExplicitlyAllowed(
136 const ExtensionId
& id
) const {
137 SettingsIdMap::const_iterator it
= settings_by_id_
.find(id
);
138 // No settings explicitly specified for |id|.
139 if (it
== settings_by_id_
.end())
141 // Checks if the extension is on the automatically installed list or
142 // install white-list.
143 InstallationMode mode
= it
->second
->installation_mode
;
144 return mode
== INSTALLATION_FORCED
|| mode
== INSTALLATION_RECOMMENDED
||
145 mode
== INSTALLATION_ALLOWED
;
148 bool ExtensionManagement::IsOffstoreInstallAllowed(
150 const GURL
& referrer_url
) const {
151 // No allowed install sites specified, disallow by default.
152 if (!global_settings_
->has_restricted_install_sources
)
155 const URLPatternSet
& url_patterns
= global_settings_
->install_sources
;
157 if (!url_patterns
.MatchesURL(url
))
160 // The referrer URL must also be whitelisted, unless the URL has the file
161 // scheme (there's no referrer for those URLs).
162 return url
.SchemeIsFile() || url_patterns
.MatchesURL(referrer_url
);
165 bool ExtensionManagement::IsAllowedManifestType(
166 Manifest::Type manifest_type
) const {
167 if (!global_settings_
->has_restricted_allowed_types
)
169 const std::vector
<Manifest::Type
>& allowed_types
=
170 global_settings_
->allowed_types
;
171 return std::find(allowed_types
.begin(), allowed_types
.end(), manifest_type
) !=
175 APIPermissionSet
ExtensionManagement::GetBlockedAPIPermissions(
176 const Extension
* extension
) const {
177 // Fetch per-extension blocked permissions setting.
178 auto iter_id
= settings_by_id_
.find(extension
->id());
180 // Fetch per-update-url blocked permissions setting.
181 std::string update_url
;
182 auto iter_update_url
= settings_by_update_url_
.end();
183 if (extension
->manifest()->GetString(manifest_keys::kUpdateURL
,
185 iter_update_url
= settings_by_update_url_
.find(update_url
);
188 if (iter_id
!= settings_by_id_
.end() &&
189 iter_update_url
!= settings_by_update_url_
.end()) {
190 // Blocked permissions setting are specified in both per-extension and
191 // per-update-url settings, try to merge them.
192 APIPermissionSet merged
;
193 APIPermissionSet::Union(iter_id
->second
->blocked_permissions
,
194 iter_update_url
->second
->blocked_permissions
,
198 // Check whether if in one of them, setting is specified.
199 if (iter_id
!= settings_by_id_
.end())
200 return iter_id
->second
->blocked_permissions
;
201 if (iter_update_url
!= settings_by_update_url_
.end())
202 return iter_update_url
->second
->blocked_permissions
;
203 // Fall back to the default blocked permissions setting.
204 return default_settings_
->blocked_permissions
;
207 scoped_refptr
<const PermissionSet
> ExtensionManagement::GetBlockedPermissions(
208 const Extension
* extension
) const {
209 // Only api permissions are supported currently.
210 return scoped_refptr
<const PermissionSet
>(new PermissionSet(
211 GetBlockedAPIPermissions(extension
), ManifestPermissionSet(),
212 URLPatternSet(), URLPatternSet()));
215 bool ExtensionManagement::IsPermissionSetAllowed(
216 const Extension
* extension
,
217 scoped_refptr
<const PermissionSet
> perms
) const {
218 for (const auto& blocked_api
: GetBlockedAPIPermissions(extension
)) {
219 if (perms
->HasAPIPermission(blocked_api
->id()))
225 bool ExtensionManagement::CheckMinimumVersion(
226 const Extension
* extension
,
227 std::string
* required_version
) const {
228 auto iter
= settings_by_id_
.find(extension
->id());
229 // If there are no minimum version required for |extension|, return true.
230 if (iter
== settings_by_id_
.end() || !iter
->second
->minimum_version_required
)
232 bool meets_requirement
= extension
->version()->CompareTo(
233 *iter
->second
->minimum_version_required
) >= 0;
234 // Output a human readable version string for prompting if necessary.
235 if (!meets_requirement
&& required_version
)
236 *required_version
= iter
->second
->minimum_version_required
->GetString();
237 return meets_requirement
;
240 void ExtensionManagement::Refresh() {
241 TRACE_EVENT0("browser,startup", "ExtensionManagement::Refresh");
242 SCOPED_UMA_HISTOGRAM_TIMER("Extensions.ExtensionManagement_RefreshTime");
243 // Load all extension management settings preferences.
244 const base::ListValue
* allowed_list_pref
=
245 static_cast<const base::ListValue
*>(LoadPreference(
246 pref_names::kInstallAllowList
, true, base::Value::TYPE_LIST
));
247 // Allow user to use preference to block certain extensions. Note that policy
248 // managed forcelist or whitelist will always override this.
249 const base::ListValue
* denied_list_pref
=
250 static_cast<const base::ListValue
*>(LoadPreference(
251 pref_names::kInstallDenyList
, false, base::Value::TYPE_LIST
));
252 const base::DictionaryValue
* forced_list_pref
=
253 static_cast<const base::DictionaryValue
*>(LoadPreference(
254 pref_names::kInstallForceList
, true, base::Value::TYPE_DICTIONARY
));
255 const base::ListValue
* install_sources_pref
=
256 static_cast<const base::ListValue
*>(LoadPreference(
257 pref_names::kAllowedInstallSites
, true, base::Value::TYPE_LIST
));
258 const base::ListValue
* allowed_types_pref
=
259 static_cast<const base::ListValue
*>(LoadPreference(
260 pref_names::kAllowedTypes
, true, base::Value::TYPE_LIST
));
261 const base::DictionaryValue
* dict_pref
=
262 static_cast<const base::DictionaryValue
*>(
263 LoadPreference(pref_names::kExtensionManagement
,
265 base::Value::TYPE_DICTIONARY
));
267 // Reset all settings.
268 global_settings_
.reset(new internal::GlobalSettings());
269 settings_by_id_
.clear();
270 default_settings_
.reset(new internal::IndividualSettings());
272 // Parse default settings.
273 const base::StringValue
wildcard("*");
274 if (denied_list_pref
&&
275 denied_list_pref
->Find(wildcard
) != denied_list_pref
->end()) {
276 default_settings_
->installation_mode
= INSTALLATION_BLOCKED
;
279 const base::DictionaryValue
* subdict
= NULL
;
281 dict_pref
->GetDictionary(schema_constants::kWildcard
, &subdict
)) {
282 if (!default_settings_
->Parse(
283 subdict
, internal::IndividualSettings::SCOPE_DEFAULT
)) {
284 LOG(WARNING
) << "Default extension management settings parsing error.";
285 default_settings_
->Reset();
288 // Settings from new preference have higher priority over legacy ones.
289 const base::ListValue
* list_value
= NULL
;
290 if (subdict
->GetList(schema_constants::kInstallSources
, &list_value
))
291 install_sources_pref
= list_value
;
292 if (subdict
->GetList(schema_constants::kAllowedTypes
, &list_value
))
293 allowed_types_pref
= list_value
;
296 // Parse legacy preferences.
299 if (allowed_list_pref
) {
300 for (base::ListValue::const_iterator it
= allowed_list_pref
->begin();
301 it
!= allowed_list_pref
->end(); ++it
) {
302 if ((*it
)->GetAsString(&id
) && crx_file::id_util::IdIsValid(id
))
303 AccessById(id
)->installation_mode
= INSTALLATION_ALLOWED
;
307 if (denied_list_pref
) {
308 for (base::ListValue::const_iterator it
= denied_list_pref
->begin();
309 it
!= denied_list_pref
->end(); ++it
) {
310 if ((*it
)->GetAsString(&id
) && crx_file::id_util::IdIsValid(id
))
311 AccessById(id
)->installation_mode
= INSTALLATION_BLOCKED
;
315 if (forced_list_pref
) {
316 std::string update_url
;
317 for (base::DictionaryValue::Iterator
it(*forced_list_pref
); !it
.IsAtEnd();
319 if (!crx_file::id_util::IdIsValid(it
.key()))
321 const base::DictionaryValue
* dict_value
= NULL
;
322 if (it
.value().GetAsDictionary(&dict_value
) &&
323 dict_value
->GetStringWithoutPathExpansion(
324 ExternalProviderImpl::kExternalUpdateUrl
, &update_url
)) {
325 internal::IndividualSettings
* by_id
= AccessById(it
.key());
326 by_id
->installation_mode
= INSTALLATION_FORCED
;
327 by_id
->update_url
= update_url
;
332 if (install_sources_pref
) {
333 global_settings_
->has_restricted_install_sources
= true;
334 for (base::ListValue::const_iterator it
= install_sources_pref
->begin();
335 it
!= install_sources_pref
->end(); ++it
) {
336 std::string url_pattern
;
337 if ((*it
)->GetAsString(&url_pattern
)) {
338 URLPattern
entry(URLPattern::SCHEME_ALL
);
339 if (entry
.Parse(url_pattern
) == URLPattern::PARSE_SUCCESS
) {
340 global_settings_
->install_sources
.AddPattern(entry
);
342 LOG(WARNING
) << "Invalid URL pattern in for preference "
343 << pref_names::kAllowedInstallSites
<< ": "
344 << url_pattern
<< ".";
350 if (allowed_types_pref
) {
351 global_settings_
->has_restricted_allowed_types
= true;
352 for (base::ListValue::const_iterator it
= allowed_types_pref
->begin();
353 it
!= allowed_types_pref
->end(); ++it
) {
355 std::string string_value
;
356 if ((*it
)->GetAsInteger(&int_value
) && int_value
>= 0 &&
357 int_value
< Manifest::Type::NUM_LOAD_TYPES
) {
358 global_settings_
->allowed_types
.push_back(
359 static_cast<Manifest::Type
>(int_value
));
360 } else if ((*it
)->GetAsString(&string_value
)) {
361 Manifest::Type manifest_type
=
362 schema_constants::GetManifestType(string_value
);
363 if (manifest_type
!= Manifest::TYPE_UNKNOWN
)
364 global_settings_
->allowed_types
.push_back(manifest_type
);
370 // Parse new extension management preference.
371 for (base::DictionaryValue::Iterator
iter(*dict_pref
); !iter
.IsAtEnd();
373 if (iter
.key() == schema_constants::kWildcard
)
375 if (!iter
.value().GetAsDictionary(&subdict
))
377 if (base::StartsWith(iter
.key(), schema_constants::kUpdateUrlPrefix
,
378 base::CompareCase::SENSITIVE
)) {
379 const std::string
& update_url
=
380 iter
.key().substr(strlen(schema_constants::kUpdateUrlPrefix
));
381 if (!GURL(update_url
).is_valid()) {
382 LOG(WARNING
) << "Invalid update URL: " << update_url
<< ".";
385 internal::IndividualSettings
* by_update_url
=
386 AccessByUpdateUrl(update_url
);
387 if (!by_update_url
->Parse(
388 subdict
, internal::IndividualSettings::SCOPE_UPDATE_URL
)) {
389 settings_by_update_url_
.erase(update_url
);
390 LOG(WARNING
) << "Malformed Extension Management settings for "
391 "extensions with update url: " << update_url
<< ".";
394 const std::string
& extension_id
= iter
.key();
395 if (!crx_file::id_util::IdIsValid(extension_id
)) {
396 LOG(WARNING
) << "Invalid extension ID : " << extension_id
<< ".";
399 internal::IndividualSettings
* by_id
= AccessById(extension_id
);
400 if (!by_id
->Parse(subdict
,
401 internal::IndividualSettings::SCOPE_INDIVIDUAL
)) {
402 settings_by_id_
.erase(extension_id
);
403 LOG(WARNING
) << "Malformed Extension Management settings for "
404 << extension_id
<< ".";
411 const base::Value
* ExtensionManagement::LoadPreference(
412 const char* pref_name
,
414 base::Value::Type expected_type
) {
417 const PrefService::Preference
* pref
=
418 pref_service_
->FindPreference(pref_name
);
419 if (pref
&& !pref
->IsDefaultValue() &&
420 (!force_managed
|| pref
->IsManaged())) {
421 const base::Value
* value
= pref
->GetValue();
422 if (value
&& value
->IsType(expected_type
))
428 void ExtensionManagement::OnExtensionPrefChanged() {
430 NotifyExtensionManagementPrefChanged();
433 void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
435 Observer
, observer_list_
, OnExtensionManagementSettingsChanged());
438 internal::IndividualSettings
* ExtensionManagement::AccessById(
439 const ExtensionId
& id
) {
440 DCHECK(crx_file::id_util::IdIsValid(id
)) << "Invalid ID: " << id
;
441 SettingsIdMap::iterator it
= settings_by_id_
.find(id
);
442 if (it
== settings_by_id_
.end()) {
443 scoped_ptr
<internal::IndividualSettings
> settings(
444 new internal::IndividualSettings(default_settings_
.get()));
445 it
= settings_by_id_
.add(id
, settings
.Pass()).first
;
450 internal::IndividualSettings
* ExtensionManagement::AccessByUpdateUrl(
451 const std::string
& update_url
) {
452 DCHECK(GURL(update_url
).is_valid()) << "Invalid update URL: " << update_url
;
453 SettingsUpdateUrlMap::iterator it
= settings_by_update_url_
.find(update_url
);
454 if (it
== settings_by_update_url_
.end()) {
455 scoped_ptr
<internal::IndividualSettings
> settings(
456 new internal::IndividualSettings(default_settings_
.get()));
457 it
= settings_by_update_url_
.add(update_url
, settings
.Pass()).first
;
462 ExtensionManagement
* ExtensionManagementFactory::GetForBrowserContext(
463 content::BrowserContext
* context
) {
464 return static_cast<ExtensionManagement
*>(
465 GetInstance()->GetServiceForBrowserContext(context
, true));
468 ExtensionManagementFactory
* ExtensionManagementFactory::GetInstance() {
469 return Singleton
<ExtensionManagementFactory
>::get();
472 ExtensionManagementFactory::ExtensionManagementFactory()
473 : BrowserContextKeyedServiceFactory(
474 "ExtensionManagement",
475 BrowserContextDependencyManager::GetInstance()) {
478 ExtensionManagementFactory::~ExtensionManagementFactory() {
481 KeyedService
* ExtensionManagementFactory::BuildServiceInstanceFor(
482 content::BrowserContext
* context
) const {
483 TRACE_EVENT0("browser,startup",
484 "ExtensionManagementFactory::BuildServiceInstanceFor");
485 return new ExtensionManagement(
486 Profile::FromBrowserContext(context
)->GetPrefs());
489 content::BrowserContext
* ExtensionManagementFactory::GetBrowserContextToUse(
490 content::BrowserContext
* context
) const {
491 return chrome::GetBrowserContextRedirectedInIncognito(context
);
494 void ExtensionManagementFactory::RegisterProfilePrefs(
495 user_prefs::PrefRegistrySyncable
* user_prefs
) {
496 user_prefs
->RegisterDictionaryPref(pref_names::kExtensionManagement
);
499 } // namespace extensions