• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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