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.
4
5 #include "chrome/browser/extensions/extension_management.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/browser/extensions/extension_management_constants.h"
13 #include "chrome/browser/extensions/external_policy_loader.h"
14 #include "chrome/browser/extensions/external_provider_impl.h"
15 #include "chrome/browser/extensions/standard_management_policy_provider.h"
16 #include "chrome/browser/profiles/incognito_helpers.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "components/crx_file/id_util.h"
19 #include "components/keyed_service/content/browser_context_dependency_manager.h"
20 #include "components/pref_registry/pref_registry_syncable.h"
21 #include "extensions/browser/pref_names.h"
22 #include "extensions/common/url_pattern.h"
23 #include "url/gurl.h"
24
25 namespace extensions {
26
27 namespace {
28
29 const char kMalformedPreferenceWarning[] =
30 "Malformed extension management preference.";
31
32 enum Scope {
33 // Parses the default settings.
34 SCOPE_DEFAULT = 0,
35 // Parses the settings for an extension with specified extension ID.
36 SCOPE_INDIVIDUAL,
37 };
38
39 // Parse the individual settings for |settings|. |dict| is the a
40 // sub-dictionary in extension management preference and |scope| represents
41 // the applicable range of the settings, a single extension, a group of
42 // extensions or default settings.
43 // Note that in case of parsing errors, |settings| will NOT be left untouched.
ParseIndividualSettings(const base::DictionaryValue * dict,Scope scope,ExtensionManagement::IndividualSettings * settings)44 bool ParseIndividualSettings(
45 const base::DictionaryValue* dict,
46 Scope scope,
47 ExtensionManagement::IndividualSettings* settings) {
48 std::string installation_mode;
49 if (dict->GetStringWithoutPathExpansion(schema_constants::kInstallationMode,
50 &installation_mode)) {
51 if (installation_mode == schema_constants::kAllowed) {
52 settings->installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
53 } else if (installation_mode == schema_constants::kBlocked) {
54 settings->installation_mode = ExtensionManagement::INSTALLATION_BLOCKED;
55 } else if (installation_mode == schema_constants::kForceInstalled) {
56 settings->installation_mode = ExtensionManagement::INSTALLATION_FORCED;
57 } else if (installation_mode == schema_constants::kNormalInstalled) {
58 settings->installation_mode =
59 ExtensionManagement::INSTALLATION_RECOMMENDED;
60 } else {
61 // Invalid value for 'installation_mode'.
62 LOG(WARNING) << kMalformedPreferenceWarning;
63 return false;
64 }
65 }
66
67 if (settings->installation_mode == ExtensionManagement::INSTALLATION_FORCED ||
68 settings->installation_mode ==
69 ExtensionManagement::INSTALLATION_RECOMMENDED) {
70 if (scope != SCOPE_INDIVIDUAL) {
71 // Only individual extensions are allowed to be automatically installed.
72 LOG(WARNING) << kMalformedPreferenceWarning;
73 return false;
74 }
75 std::string update_url;
76 if (dict->GetStringWithoutPathExpansion(schema_constants::kUpdateUrl,
77 &update_url) &&
78 GURL(update_url).is_valid()) {
79 settings->update_url = update_url;
80 } else {
81 // No valid update URL for extension.
82 LOG(WARNING) << kMalformedPreferenceWarning;
83 return false;
84 }
85 }
86
87 return true;
88 }
89
90 } // namespace
91
IndividualSettings()92 ExtensionManagement::IndividualSettings::IndividualSettings() {
93 Reset();
94 }
95
~IndividualSettings()96 ExtensionManagement::IndividualSettings::~IndividualSettings() {
97 }
98
Reset()99 void ExtensionManagement::IndividualSettings::Reset() {
100 installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
101 update_url.clear();
102 }
103
GlobalSettings()104 ExtensionManagement::GlobalSettings::GlobalSettings() {
105 Reset();
106 }
107
~GlobalSettings()108 ExtensionManagement::GlobalSettings::~GlobalSettings() {
109 }
110
Reset()111 void ExtensionManagement::GlobalSettings::Reset() {
112 has_restricted_install_sources = false;
113 install_sources.ClearPatterns();
114 has_restricted_allowed_types = false;
115 allowed_types.clear();
116 }
117
ExtensionManagement(PrefService * pref_service)118 ExtensionManagement::ExtensionManagement(PrefService* pref_service)
119 : pref_service_(pref_service) {
120 pref_change_registrar_.Init(pref_service_);
121 base::Closure pref_change_callback = base::Bind(
122 &ExtensionManagement::OnExtensionPrefChanged, base::Unretained(this));
123 pref_change_registrar_.Add(pref_names::kInstallAllowList,
124 pref_change_callback);
125 pref_change_registrar_.Add(pref_names::kInstallDenyList,
126 pref_change_callback);
127 pref_change_registrar_.Add(pref_names::kInstallForceList,
128 pref_change_callback);
129 pref_change_registrar_.Add(pref_names::kAllowedInstallSites,
130 pref_change_callback);
131 pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback);
132 pref_change_registrar_.Add(pref_names::kExtensionManagement,
133 pref_change_callback);
134 Refresh();
135 provider_.reset(new StandardManagementPolicyProvider(this));
136 }
137
~ExtensionManagement()138 ExtensionManagement::~ExtensionManagement() {
139 }
140
AddObserver(Observer * observer)141 void ExtensionManagement::AddObserver(Observer* observer) {
142 observer_list_.AddObserver(observer);
143 }
144
RemoveObserver(Observer * observer)145 void ExtensionManagement::RemoveObserver(Observer* observer) {
146 observer_list_.RemoveObserver(observer);
147 }
148
GetProvider()149 ManagementPolicy::Provider* ExtensionManagement::GetProvider() {
150 return provider_.get();
151 }
152
BlacklistedByDefault()153 bool ExtensionManagement::BlacklistedByDefault() {
154 return default_settings_.installation_mode == INSTALLATION_BLOCKED;
155 }
156
GetForceInstallList() const157 scoped_ptr<base::DictionaryValue> ExtensionManagement::GetForceInstallList()
158 const {
159 scoped_ptr<base::DictionaryValue> forcelist(new base::DictionaryValue());
160 for (SettingsIdMap::const_iterator it = settings_by_id_.begin();
161 it != settings_by_id_.end();
162 ++it) {
163 if (it->second.installation_mode == INSTALLATION_FORCED) {
164 ExternalPolicyLoader::AddExtension(
165 forcelist.get(), it->first, it->second.update_url);
166 }
167 }
168 return forcelist.Pass();
169 }
170
IsInstallationExplicitlyAllowed(const ExtensionId & id) const171 bool ExtensionManagement::IsInstallationExplicitlyAllowed(
172 const ExtensionId& id) const {
173 SettingsIdMap::const_iterator it = settings_by_id_.find(id);
174 // No settings explicitly specified for |id|.
175 if (it == settings_by_id_.end())
176 return false;
177 // Checks if the extension is on the automatically installed list or
178 // install white-list.
179 InstallationMode mode = it->second.installation_mode;
180 return mode == INSTALLATION_FORCED || mode == INSTALLATION_RECOMMENDED ||
181 mode == INSTALLATION_ALLOWED;
182 }
183
IsOffstoreInstallAllowed(const GURL & url,const GURL & referrer_url)184 bool ExtensionManagement::IsOffstoreInstallAllowed(const GURL& url,
185 const GURL& referrer_url) {
186 // No allowed install sites specified, disallow by default.
187 if (!global_settings_.has_restricted_install_sources)
188 return false;
189
190 const extensions::URLPatternSet& url_patterns =
191 global_settings_.install_sources;
192
193 if (!url_patterns.MatchesURL(url))
194 return false;
195
196 // The referrer URL must also be whitelisted, unless the URL has the file
197 // scheme (there's no referrer for those URLs).
198 return url.SchemeIsFile() || url_patterns.MatchesURL(referrer_url);
199 }
200
ReadById(const ExtensionId & id) const201 const ExtensionManagement::IndividualSettings& ExtensionManagement::ReadById(
202 const ExtensionId& id) const {
203 DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
204 SettingsIdMap::const_iterator it = settings_by_id_.find(id);
205 if (it != settings_by_id_.end())
206 return it->second;
207 return default_settings_;
208 }
209
210 const ExtensionManagement::GlobalSettings&
ReadGlobalSettings() const211 ExtensionManagement::ReadGlobalSettings() const {
212 return global_settings_;
213 }
214
Refresh()215 void ExtensionManagement::Refresh() {
216 // Load all extension management settings preferences.
217 const base::ListValue* allowed_list_pref =
218 static_cast<const base::ListValue*>(LoadPreference(
219 pref_names::kInstallAllowList, true, base::Value::TYPE_LIST));
220 // Allow user to use preference to block certain extensions. Note that policy
221 // managed forcelist or whitelist will always override this.
222 const base::ListValue* denied_list_pref =
223 static_cast<const base::ListValue*>(LoadPreference(
224 pref_names::kInstallDenyList, false, base::Value::TYPE_LIST));
225 const base::DictionaryValue* forced_list_pref =
226 static_cast<const base::DictionaryValue*>(LoadPreference(
227 pref_names::kInstallForceList, true, base::Value::TYPE_DICTIONARY));
228 const base::ListValue* install_sources_pref =
229 static_cast<const base::ListValue*>(LoadPreference(
230 pref_names::kAllowedInstallSites, true, base::Value::TYPE_LIST));
231 const base::ListValue* allowed_types_pref =
232 static_cast<const base::ListValue*>(LoadPreference(
233 pref_names::kAllowedTypes, true, base::Value::TYPE_LIST));
234 const base::DictionaryValue* dict_pref =
235 static_cast<const base::DictionaryValue*>(
236 LoadPreference(pref_names::kExtensionManagement,
237 true,
238 base::Value::TYPE_DICTIONARY));
239
240 // Reset all settings.
241 global_settings_.Reset();
242 settings_by_id_.clear();
243 default_settings_.Reset();
244
245 // Parse default settings.
246 const base::StringValue wildcard("*");
247 if (denied_list_pref &&
248 denied_list_pref->Find(wildcard) != denied_list_pref->end()) {
249 default_settings_.installation_mode = INSTALLATION_BLOCKED;
250 }
251
252 const base::DictionaryValue* subdict = NULL;
253 if (dict_pref &&
254 dict_pref->GetDictionary(schema_constants::kWildcard, &subdict)) {
255 if (!ParseIndividualSettings(subdict, SCOPE_DEFAULT, &default_settings_)) {
256 LOG(WARNING) << "Default extension management settings parsing error.";
257 default_settings_.Reset();
258 }
259
260 // Settings from new preference have higher priority over legacy ones.
261 const base::ListValue* list_value = NULL;
262 if (subdict->GetList(schema_constants::kInstallSources, &list_value))
263 install_sources_pref = list_value;
264 if (subdict->GetList(schema_constants::kAllowedTypes, &list_value))
265 allowed_types_pref = list_value;
266 }
267
268 // Parse legacy preferences.
269 ExtensionId id;
270
271 if (allowed_list_pref) {
272 for (base::ListValue::const_iterator it = allowed_list_pref->begin();
273 it != allowed_list_pref->end(); ++it) {
274 if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
275 AccessById(id)->installation_mode = INSTALLATION_ALLOWED;
276 }
277 }
278
279 if (denied_list_pref) {
280 for (base::ListValue::const_iterator it = denied_list_pref->begin();
281 it != denied_list_pref->end(); ++it) {
282 if ((*it)->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
283 AccessById(id)->installation_mode = INSTALLATION_BLOCKED;
284 }
285 }
286
287 if (forced_list_pref) {
288 std::string update_url;
289 for (base::DictionaryValue::Iterator it(*forced_list_pref); !it.IsAtEnd();
290 it.Advance()) {
291 if (!crx_file::id_util::IdIsValid(it.key()))
292 continue;
293 const base::DictionaryValue* dict_value = NULL;
294 if (it.value().GetAsDictionary(&dict_value) &&
295 dict_value->GetStringWithoutPathExpansion(
296 ExternalProviderImpl::kExternalUpdateUrl, &update_url)) {
297 IndividualSettings* by_id = AccessById(it.key());
298 by_id->installation_mode = INSTALLATION_FORCED;
299 by_id->update_url = update_url;
300 }
301 }
302 }
303
304 if (install_sources_pref) {
305 global_settings_.has_restricted_install_sources = true;
306 for (base::ListValue::const_iterator it = install_sources_pref->begin();
307 it != install_sources_pref->end(); ++it) {
308 std::string url_pattern;
309 if ((*it)->GetAsString(&url_pattern)) {
310 URLPattern entry(URLPattern::SCHEME_ALL);
311 if (entry.Parse(url_pattern) == URLPattern::PARSE_SUCCESS) {
312 global_settings_.install_sources.AddPattern(entry);
313 } else {
314 LOG(WARNING) << "Invalid URL pattern in for preference "
315 << pref_names::kAllowedInstallSites << ": "
316 << url_pattern << ".";
317 }
318 }
319 }
320 }
321
322 if (allowed_types_pref) {
323 global_settings_.has_restricted_allowed_types = true;
324 for (base::ListValue::const_iterator it = allowed_types_pref->begin();
325 it != allowed_types_pref->end(); ++it) {
326 int int_value;
327 std::string string_value;
328 if ((*it)->GetAsInteger(&int_value) && int_value >= 0 &&
329 int_value < Manifest::Type::NUM_LOAD_TYPES) {
330 global_settings_.allowed_types.push_back(
331 static_cast<Manifest::Type>(int_value));
332 } else if ((*it)->GetAsString(&string_value)) {
333 Manifest::Type manifest_type =
334 schema_constants::GetManifestType(string_value);
335 if (manifest_type != Manifest::TYPE_UNKNOWN)
336 global_settings_.allowed_types.push_back(manifest_type);
337 }
338 }
339 }
340
341 if (dict_pref) {
342 // Parse new extension management preference.
343 for (base::DictionaryValue::Iterator iter(*dict_pref); !iter.IsAtEnd();
344 iter.Advance()) {
345 if (iter.key() == schema_constants::kWildcard)
346 continue;
347 if (!iter.value().GetAsDictionary(&subdict)) {
348 LOG(WARNING) << kMalformedPreferenceWarning;
349 continue;
350 }
351 if (StartsWithASCII(iter.key(), schema_constants::kUpdateUrlPrefix, true))
352 continue;
353 const std::string& extension_id = iter.key();
354 if (!crx_file::id_util::IdIsValid(extension_id)) {
355 LOG(WARNING) << kMalformedPreferenceWarning;
356 continue;
357 }
358 IndividualSettings* by_id = AccessById(extension_id);
359 if (!ParseIndividualSettings(subdict, SCOPE_INDIVIDUAL, by_id)) {
360 settings_by_id_.erase(settings_by_id_.find(extension_id));
361 LOG(WARNING) << "Malformed Extension Management settings for "
362 << extension_id << ".";
363 }
364 }
365 }
366 }
367
LoadPreference(const char * pref_name,bool force_managed,base::Value::Type expected_type)368 const base::Value* ExtensionManagement::LoadPreference(
369 const char* pref_name,
370 bool force_managed,
371 base::Value::Type expected_type) {
372 const PrefService::Preference* pref =
373 pref_service_->FindPreference(pref_name);
374 if (pref && !pref->IsDefaultValue() &&
375 (!force_managed || pref->IsManaged())) {
376 const base::Value* value = pref->GetValue();
377 if (value && value->IsType(expected_type))
378 return value;
379 }
380 return NULL;
381 }
382
OnExtensionPrefChanged()383 void ExtensionManagement::OnExtensionPrefChanged() {
384 Refresh();
385 NotifyExtensionManagementPrefChanged();
386 }
387
NotifyExtensionManagementPrefChanged()388 void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
389 FOR_EACH_OBSERVER(
390 Observer, observer_list_, OnExtensionManagementSettingsChanged());
391 }
392
AccessById(const ExtensionId & id)393 ExtensionManagement::IndividualSettings* ExtensionManagement::AccessById(
394 const ExtensionId& id) {
395 DCHECK(crx_file::id_util::IdIsValid(id)) << "Invalid ID: " << id;
396 SettingsIdMap::iterator it = settings_by_id_.find(id);
397 if (it == settings_by_id_.end())
398 it = settings_by_id_.insert(std::make_pair(id, default_settings_)).first;
399 return &it->second;
400 }
401
GetForBrowserContext(content::BrowserContext * context)402 ExtensionManagement* ExtensionManagementFactory::GetForBrowserContext(
403 content::BrowserContext* context) {
404 return static_cast<ExtensionManagement*>(
405 GetInstance()->GetServiceForBrowserContext(context, true));
406 }
407
GetInstance()408 ExtensionManagementFactory* ExtensionManagementFactory::GetInstance() {
409 return Singleton<ExtensionManagementFactory>::get();
410 }
411
ExtensionManagementFactory()412 ExtensionManagementFactory::ExtensionManagementFactory()
413 : BrowserContextKeyedServiceFactory(
414 "ExtensionManagement",
415 BrowserContextDependencyManager::GetInstance()) {
416 }
417
~ExtensionManagementFactory()418 ExtensionManagementFactory::~ExtensionManagementFactory() {
419 }
420
BuildServiceInstanceFor(content::BrowserContext * context) const421 KeyedService* ExtensionManagementFactory::BuildServiceInstanceFor(
422 content::BrowserContext* context) const {
423 return new ExtensionManagement(
424 Profile::FromBrowserContext(context)->GetPrefs());
425 }
426
GetBrowserContextToUse(content::BrowserContext * context) const427 content::BrowserContext* ExtensionManagementFactory::GetBrowserContextToUse(
428 content::BrowserContext* context) const {
429 return chrome::GetBrowserContextRedirectedInIncognito(context);
430 }
431
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * user_prefs)432 void ExtensionManagementFactory::RegisterProfilePrefs(
433 user_prefs::PrefRegistrySyncable* user_prefs) {
434 user_prefs->RegisterDictionaryPref(
435 pref_names::kExtensionManagement,
436 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
437 }
438
439 } // namespace extensions
440