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/prefs/pref_service.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_util.h"
15 #include "base/version.h"
16 #include "chrome/browser/extensions/extension_management_constants.h"
17 #include "chrome/browser/extensions/extension_management_internal.h"
18 #include "chrome/browser/extensions/external_policy_loader.h"
19 #include "chrome/browser/extensions/external_provider_impl.h"
20 #include "chrome/browser/extensions/permissions_based_management_policy_provider.h"
21 #include "chrome/browser/extensions/standard_management_policy_provider.h"
22 #include "chrome/browser/profiles/incognito_helpers.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "components/crx_file/id_util.h"
25 #include "components/keyed_service/content/browser_context_dependency_manager.h"
26 #include "components/pref_registry/pref_registry_syncable.h"
27 #include "extensions/browser/pref_names.h"
28 #include "extensions/common/manifest_constants.h"
29 #include "extensions/common/permissions/api_permission_set.h"
30 #include "extensions/common/permissions/permission_set.h"
31 #include "extensions/common/url_pattern.h"
34 namespace extensions
{
36 ExtensionManagement::ExtensionManagement(PrefService
* pref_service
)
37 : pref_service_(pref_service
) {
38 pref_change_registrar_
.Init(pref_service_
);
39 base::Closure pref_change_callback
= base::Bind(
40 &ExtensionManagement::OnExtensionPrefChanged
, base::Unretained(this));
41 pref_change_registrar_
.Add(pref_names::kInstallAllowList
,
42 pref_change_callback
);
43 pref_change_registrar_
.Add(pref_names::kInstallDenyList
,
44 pref_change_callback
);
45 pref_change_registrar_
.Add(pref_names::kInstallForceList
,
46 pref_change_callback
);
47 pref_change_registrar_
.Add(pref_names::kAllowedInstallSites
,
48 pref_change_callback
);
49 pref_change_registrar_
.Add(pref_names::kAllowedTypes
, pref_change_callback
);
50 pref_change_registrar_
.Add(pref_names::kExtensionManagement
,
51 pref_change_callback
);
52 // Note that both |global_settings_| and |default_settings_| will be null
53 // before first call to Refresh(), so in order to resolve this, Refresh() must
54 // be called in the initialization of ExtensionManagement.
56 providers_
.push_back(new StandardManagementPolicyProvider(this));
57 providers_
.push_back(new PermissionsBasedManagementPolicyProvider(this));
60 ExtensionManagement::~ExtensionManagement() {
63 void ExtensionManagement::Shutdown() {
64 pref_change_registrar_
.RemoveAll();
65 pref_service_
= nullptr;
68 void ExtensionManagement::AddObserver(Observer
* observer
) {
69 observer_list_
.AddObserver(observer
);
72 void ExtensionManagement::RemoveObserver(Observer
* observer
) {
73 observer_list_
.RemoveObserver(observer
);
76 std::vector
<ManagementPolicy::Provider
*> ExtensionManagement::GetProviders()
78 return providers_
.get();
81 bool ExtensionManagement::BlacklistedByDefault() const {
82 return default_settings_
->installation_mode
== INSTALLATION_BLOCKED
;
85 ExtensionManagement::InstallationMode
ExtensionManagement::GetInstallationMode(
86 const Extension
* extension
) const {
87 // Check per-extension installation mode setting first.
88 auto iter_id
= settings_by_id_
.find(extension
->id());
89 if (iter_id
!= settings_by_id_
.end())
90 return iter_id
->second
->installation_mode
;
91 std::string update_url
;
92 // Check per-update-url installation mode setting.
93 if (extension
->manifest()->GetString(manifest_keys::kUpdateURL
,
95 auto iter_update_url
= settings_by_update_url_
.find(update_url
);
96 if (iter_update_url
!= settings_by_update_url_
.end())
97 return iter_update_url
->second
->installation_mode
;
99 // Fall back to default installation mode setting.
100 return default_settings_
->installation_mode
;
103 scoped_ptr
<base::DictionaryValue
> ExtensionManagement::GetForceInstallList()
105 scoped_ptr
<base::DictionaryValue
> install_list(new base::DictionaryValue());
106 for (SettingsIdMap::const_iterator it
= settings_by_id_
.begin();
107 it
!= settings_by_id_
.end();
109 if (it
->second
->installation_mode
== INSTALLATION_FORCED
) {
110 ExternalPolicyLoader::AddExtension(
111 install_list
.get(), it
->first
, it
->second
->update_url
);
114 return install_list
.Pass();
117 scoped_ptr
<base::DictionaryValue
>
118 ExtensionManagement::GetRecommendedInstallList() const {
119 scoped_ptr
<base::DictionaryValue
> install_list(new base::DictionaryValue());
120 for (SettingsIdMap::const_iterator it
= settings_by_id_
.begin();
121 it
!= settings_by_id_
.end();
123 if (it
->second
->installation_mode
== INSTALLATION_RECOMMENDED
) {
124 ExternalPolicyLoader::AddExtension(
125 install_list
.get(), it
->first
, it
->second
->update_url
);
128 return install_list
.Pass();
131 bool ExtensionManagement::IsInstallationExplicitlyAllowed(
132 const ExtensionId
& id
) const {
133 SettingsIdMap::const_iterator it
= settings_by_id_
.find(id
);
134 // No settings explicitly specified for |id|.
135 if (it
== settings_by_id_
.end())
137 // Checks if the extension is on the automatically installed list or
138 // install white-list.
139 InstallationMode mode
= it
->second
->installation_mode
;
140 return mode
== INSTALLATION_FORCED
|| mode
== INSTALLATION_RECOMMENDED
||
141 mode
== INSTALLATION_ALLOWED
;
144 bool ExtensionManagement::IsOffstoreInstallAllowed(
146 const GURL
& referrer_url
) const {
147 // No allowed install sites specified, disallow by default.
148 if (!global_settings_
->has_restricted_install_sources
)
151 const URLPatternSet
& url_patterns
= global_settings_
->install_sources
;
153 if (!url_patterns
.MatchesURL(url
))
156 // The referrer URL must also be whitelisted, unless the URL has the file
157 // scheme (there's no referrer for those URLs).
158 return url
.SchemeIsFile() || url_patterns
.MatchesURL(referrer_url
);
161 bool ExtensionManagement::IsAllowedManifestType(
162 Manifest::Type manifest_type
) const {
163 if (!global_settings_
->has_restricted_allowed_types
)
165 const std::vector
<Manifest::Type
>& allowed_types
=
166 global_settings_
->allowed_types
;
167 return std::find(allowed_types
.begin(), allowed_types
.end(), manifest_type
) !=
171 APIPermissionSet
ExtensionManagement::GetBlockedAPIPermissions(
172 const Extension
* extension
) const {
173 // Fetch per-extension blocked permissions setting.
174 auto iter_id
= settings_by_id_
.find(extension
->id());
176 // Fetch per-update-url blocked permissions setting.
177 std::string update_url
;
178 auto iter_update_url
= settings_by_update_url_
.end();
179 if (extension
->manifest()->GetString(manifest_keys::kUpdateURL
,
181 iter_update_url
= settings_by_update_url_
.find(update_url
);
184 if (iter_id
!= settings_by_id_
.end() &&
185 iter_update_url
!= settings_by_update_url_
.end()) {
186 // Blocked permissions setting are specified in both per-extension and
187 // per-update-url settings, try to merge them.
188 APIPermissionSet merged
;
189 APIPermissionSet::Union(iter_id
->second
->blocked_permissions
,
190 iter_update_url
->second
->blocked_permissions
,
194 // Check whether if in one of them, setting is specified.
195 if (iter_id
!= settings_by_id_
.end())
196 return iter_id
->second
->blocked_permissions
;
197 if (iter_update_url
!= settings_by_update_url_
.end())
198 return iter_update_url
->second
->blocked_permissions
;
199 // Fall back to the default blocked permissions setting.
200 return default_settings_
->blocked_permissions
;
203 scoped_refptr
<const PermissionSet
> ExtensionManagement::GetBlockedPermissions(
204 const Extension
* extension
) const {
205 // Only api permissions are supported currently.
206 return scoped_refptr
<const PermissionSet
>(new PermissionSet(
207 GetBlockedAPIPermissions(extension
), ManifestPermissionSet(),
208 URLPatternSet(), URLPatternSet()));
211 bool ExtensionManagement::IsPermissionSetAllowed(
212 const Extension
* extension
,
213 scoped_refptr
<const PermissionSet
> perms
) const {
214 for (const auto& blocked_api
: GetBlockedAPIPermissions(extension
)) {
215 if (perms
->HasAPIPermission(blocked_api
->id()))
221 bool ExtensionManagement::CheckMinimumVersion(
222 const Extension
* extension
,
223 std::string
* required_version
) const {
224 auto iter
= settings_by_id_
.find(extension
->id());
225 // If there are no minimum version required for |extension|, return true.
226 if (iter
== settings_by_id_
.end() || !iter
->second
->minimum_version_required
)
228 bool meets_requirement
= extension
->version()->CompareTo(
229 *iter
->second
->minimum_version_required
) >= 0;
230 // Output a human readable version string for prompting if necessary.
231 if (!meets_requirement
&& required_version
)
232 *required_version
= iter
->second
->minimum_version_required
->GetString();
233 return meets_requirement
;
236 void ExtensionManagement::Refresh() {
237 // Load all extension management settings preferences.
238 const base::ListValue
* allowed_list_pref
=
239 static_cast<const base::ListValue
*>(LoadPreference(
240 pref_names::kInstallAllowList
, true, base::Value::TYPE_LIST
));
241 // Allow user to use preference to block certain extensions. Note that policy
242 // managed forcelist or whitelist will always override this.
243 const base::ListValue
* denied_list_pref
=
244 static_cast<const base::ListValue
*>(LoadPreference(
245 pref_names::kInstallDenyList
, false, base::Value::TYPE_LIST
));
246 const base::DictionaryValue
* forced_list_pref
=
247 static_cast<const base::DictionaryValue
*>(LoadPreference(
248 pref_names::kInstallForceList
, true, base::Value::TYPE_DICTIONARY
));
249 const base::ListValue
* install_sources_pref
=
250 static_cast<const base::ListValue
*>(LoadPreference(
251 pref_names::kAllowedInstallSites
, true, base::Value::TYPE_LIST
));
252 const base::ListValue
* allowed_types_pref
=
253 static_cast<const base::ListValue
*>(LoadPreference(
254 pref_names::kAllowedTypes
, true, base::Value::TYPE_LIST
));
255 const base::DictionaryValue
* dict_pref
=
256 static_cast<const base::DictionaryValue
*>(
257 LoadPreference(pref_names::kExtensionManagement
,
259 base::Value::TYPE_DICTIONARY
));
261 // Reset all settings.
262 global_settings_
.reset(new internal::GlobalSettings());
263 settings_by_id_
.clear();
264 default_settings_
.reset(new internal::IndividualSettings());
266 // Parse default settings.
267 const base::StringValue
wildcard("*");
268 if (denied_list_pref
&&
269 denied_list_pref
->Find(wildcard
) != denied_list_pref
->end()) {
270 default_settings_
->installation_mode
= INSTALLATION_BLOCKED
;
273 const base::DictionaryValue
* subdict
= NULL
;
275 dict_pref
->GetDictionary(schema_constants::kWildcard
, &subdict
)) {
276 if (!default_settings_
->Parse(
277 subdict
, internal::IndividualSettings::SCOPE_DEFAULT
)) {
278 LOG(WARNING
) << "Default extension management settings parsing error.";
279 default_settings_
->Reset();
282 // Settings from new preference have higher priority over legacy ones.
283 const base::ListValue
* list_value
= NULL
;
284 if (subdict
->GetList(schema_constants::kInstallSources
, &list_value
))
285 install_sources_pref
= list_value
;
286 if (subdict
->GetList(schema_constants::kAllowedTypes
, &list_value
))
287 allowed_types_pref
= list_value
;
290 // Parse legacy preferences.
293 if (allowed_list_pref
) {
294 for (base::ListValue::const_iterator it
= allowed_list_pref
->begin();
295 it
!= allowed_list_pref
->end(); ++it
) {
296 if ((*it
)->GetAsString(&id
) && crx_file::id_util::IdIsValid(id
))
297 AccessById(id
)->installation_mode
= INSTALLATION_ALLOWED
;
301 if (denied_list_pref
) {
302 for (base::ListValue::const_iterator it
= denied_list_pref
->begin();
303 it
!= denied_list_pref
->end(); ++it
) {
304 if ((*it
)->GetAsString(&id
) && crx_file::id_util::IdIsValid(id
))
305 AccessById(id
)->installation_mode
= INSTALLATION_BLOCKED
;
309 if (forced_list_pref
) {
310 std::string update_url
;
311 for (base::DictionaryValue::Iterator
it(*forced_list_pref
); !it
.IsAtEnd();
313 if (!crx_file::id_util::IdIsValid(it
.key()))
315 const base::DictionaryValue
* dict_value
= NULL
;
316 if (it
.value().GetAsDictionary(&dict_value
) &&
317 dict_value
->GetStringWithoutPathExpansion(
318 ExternalProviderImpl::kExternalUpdateUrl
, &update_url
)) {
319 internal::IndividualSettings
* by_id
= AccessById(it
.key());
320 by_id
->installation_mode
= INSTALLATION_FORCED
;
321 by_id
->update_url
= update_url
;
326 if (install_sources_pref
) {
327 global_settings_
->has_restricted_install_sources
= true;
328 for (base::ListValue::const_iterator it
= install_sources_pref
->begin();
329 it
!= install_sources_pref
->end(); ++it
) {
330 std::string url_pattern
;
331 if ((*it
)->GetAsString(&url_pattern
)) {
332 URLPattern
entry(URLPattern::SCHEME_ALL
);
333 if (entry
.Parse(url_pattern
) == URLPattern::PARSE_SUCCESS
) {
334 global_settings_
->install_sources
.AddPattern(entry
);
336 LOG(WARNING
) << "Invalid URL pattern in for preference "
337 << pref_names::kAllowedInstallSites
<< ": "
338 << url_pattern
<< ".";
344 if (allowed_types_pref
) {
345 global_settings_
->has_restricted_allowed_types
= true;
346 for (base::ListValue::const_iterator it
= allowed_types_pref
->begin();
347 it
!= allowed_types_pref
->end(); ++it
) {
349 std::string string_value
;
350 if ((*it
)->GetAsInteger(&int_value
) && int_value
>= 0 &&
351 int_value
< Manifest::Type::NUM_LOAD_TYPES
) {
352 global_settings_
->allowed_types
.push_back(
353 static_cast<Manifest::Type
>(int_value
));
354 } else if ((*it
)->GetAsString(&string_value
)) {
355 Manifest::Type manifest_type
=
356 schema_constants::GetManifestType(string_value
);
357 if (manifest_type
!= Manifest::TYPE_UNKNOWN
)
358 global_settings_
->allowed_types
.push_back(manifest_type
);
364 // Parse new extension management preference.
365 for (base::DictionaryValue::Iterator
iter(*dict_pref
); !iter
.IsAtEnd();
367 if (iter
.key() == schema_constants::kWildcard
)
369 if (!iter
.value().GetAsDictionary(&subdict
))
371 if (StartsWithASCII(iter
.key(), schema_constants::kUpdateUrlPrefix
,
373 const std::string
& update_url
=
374 iter
.key().substr(strlen(schema_constants::kUpdateUrlPrefix
));
375 if (!GURL(update_url
).is_valid()) {
376 LOG(WARNING
) << "Invalid update URL: " << update_url
<< ".";
379 internal::IndividualSettings
* by_update_url
=
380 AccessByUpdateUrl(update_url
);
381 if (!by_update_url
->Parse(
382 subdict
, internal::IndividualSettings::SCOPE_UPDATE_URL
)) {
383 settings_by_update_url_
.erase(update_url
);
384 LOG(WARNING
) << "Malformed Extension Management settings for "
385 "extensions with update url: " << update_url
<< ".";
388 const std::string
& extension_id
= iter
.key();
389 if (!crx_file::id_util::IdIsValid(extension_id
)) {
390 LOG(WARNING
) << "Invalid extension ID : " << extension_id
<< ".";
393 internal::IndividualSettings
* by_id
= AccessById(extension_id
);
394 if (!by_id
->Parse(subdict
,
395 internal::IndividualSettings::SCOPE_INDIVIDUAL
)) {
396 settings_by_id_
.erase(extension_id
);
397 LOG(WARNING
) << "Malformed Extension Management settings for "
398 << extension_id
<< ".";
405 const base::Value
* ExtensionManagement::LoadPreference(
406 const char* pref_name
,
408 base::Value::Type expected_type
) {
411 const PrefService::Preference
* pref
=
412 pref_service_
->FindPreference(pref_name
);
413 if (pref
&& !pref
->IsDefaultValue() &&
414 (!force_managed
|| pref
->IsManaged())) {
415 const base::Value
* value
= pref
->GetValue();
416 if (value
&& value
->IsType(expected_type
))
422 void ExtensionManagement::OnExtensionPrefChanged() {
424 NotifyExtensionManagementPrefChanged();
427 void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
429 Observer
, observer_list_
, OnExtensionManagementSettingsChanged());
432 internal::IndividualSettings
* ExtensionManagement::AccessById(
433 const ExtensionId
& id
) {
434 DCHECK(crx_file::id_util::IdIsValid(id
)) << "Invalid ID: " << id
;
435 SettingsIdMap::iterator it
= settings_by_id_
.find(id
);
436 if (it
== settings_by_id_
.end()) {
437 scoped_ptr
<internal::IndividualSettings
> settings(
438 new internal::IndividualSettings(default_settings_
.get()));
439 it
= settings_by_id_
.add(id
, settings
.Pass()).first
;
444 internal::IndividualSettings
* ExtensionManagement::AccessByUpdateUrl(
445 const std::string
& update_url
) {
446 DCHECK(GURL(update_url
).is_valid()) << "Invalid update URL: " << update_url
;
447 SettingsUpdateUrlMap::iterator it
= settings_by_update_url_
.find(update_url
);
448 if (it
== settings_by_update_url_
.end()) {
449 scoped_ptr
<internal::IndividualSettings
> settings(
450 new internal::IndividualSettings(default_settings_
.get()));
451 it
= settings_by_update_url_
.add(update_url
, settings
.Pass()).first
;
456 ExtensionManagement
* ExtensionManagementFactory::GetForBrowserContext(
457 content::BrowserContext
* context
) {
458 return static_cast<ExtensionManagement
*>(
459 GetInstance()->GetServiceForBrowserContext(context
, true));
462 ExtensionManagementFactory
* ExtensionManagementFactory::GetInstance() {
463 return Singleton
<ExtensionManagementFactory
>::get();
466 ExtensionManagementFactory::ExtensionManagementFactory()
467 : BrowserContextKeyedServiceFactory(
468 "ExtensionManagement",
469 BrowserContextDependencyManager::GetInstance()) {
472 ExtensionManagementFactory::~ExtensionManagementFactory() {
475 KeyedService
* ExtensionManagementFactory::BuildServiceInstanceFor(
476 content::BrowserContext
* context
) const {
477 return new ExtensionManagement(
478 Profile::FromBrowserContext(context
)->GetPrefs());
481 content::BrowserContext
* ExtensionManagementFactory::GetBrowserContextToUse(
482 content::BrowserContext
* context
) const {
483 return chrome::GetBrowserContextRedirectedInIncognito(context
);
486 void ExtensionManagementFactory::RegisterProfilePrefs(
487 user_prefs::PrefRegistrySyncable
* user_prefs
) {
488 user_prefs
->RegisterDictionaryPref(
489 pref_names::kExtensionManagement
,
490 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
493 } // namespace extensions