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
25namespace extensions {
26
27namespace {
28
29const char kMalformedPreferenceWarning[] =
30    "Malformed extension management preference.";
31
32enum 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.
44bool 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
92ExtensionManagement::IndividualSettings::IndividualSettings() {
93  Reset();
94}
95
96ExtensionManagement::IndividualSettings::~IndividualSettings() {
97}
98
99void ExtensionManagement::IndividualSettings::Reset() {
100  installation_mode = ExtensionManagement::INSTALLATION_ALLOWED;
101  update_url.clear();
102}
103
104ExtensionManagement::GlobalSettings::GlobalSettings() {
105  Reset();
106}
107
108ExtensionManagement::GlobalSettings::~GlobalSettings() {
109}
110
111void 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
118ExtensionManagement::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
138ExtensionManagement::~ExtensionManagement() {
139}
140
141void ExtensionManagement::AddObserver(Observer* observer) {
142  observer_list_.AddObserver(observer);
143}
144
145void ExtensionManagement::RemoveObserver(Observer* observer) {
146  observer_list_.RemoveObserver(observer);
147}
148
149ManagementPolicy::Provider* ExtensionManagement::GetProvider() {
150  return provider_.get();
151}
152
153bool ExtensionManagement::BlacklistedByDefault() {
154  return default_settings_.installation_mode == INSTALLATION_BLOCKED;
155}
156
157scoped_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
171bool 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
184bool 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
201const 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
210const ExtensionManagement::GlobalSettings&
211ExtensionManagement::ReadGlobalSettings() const {
212  return global_settings_;
213}
214
215void 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
368const 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
383void ExtensionManagement::OnExtensionPrefChanged() {
384  Refresh();
385  NotifyExtensionManagementPrefChanged();
386}
387
388void ExtensionManagement::NotifyExtensionManagementPrefChanged() {
389  FOR_EACH_OBSERVER(
390      Observer, observer_list_, OnExtensionManagementSettingsChanged());
391}
392
393ExtensionManagement::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
402ExtensionManagement* ExtensionManagementFactory::GetForBrowserContext(
403    content::BrowserContext* context) {
404  return static_cast<ExtensionManagement*>(
405      GetInstance()->GetServiceForBrowserContext(context, true));
406}
407
408ExtensionManagementFactory* ExtensionManagementFactory::GetInstance() {
409  return Singleton<ExtensionManagementFactory>::get();
410}
411
412ExtensionManagementFactory::ExtensionManagementFactory()
413    : BrowserContextKeyedServiceFactory(
414          "ExtensionManagement",
415          BrowserContextDependencyManager::GetInstance()) {
416}
417
418ExtensionManagementFactory::~ExtensionManagementFactory() {
419}
420
421KeyedService* ExtensionManagementFactory::BuildServiceInstanceFor(
422    content::BrowserContext* context) const {
423  return new ExtensionManagement(
424      Profile::FromBrowserContext(context)->GetPrefs());
425}
426
427content::BrowserContext* ExtensionManagementFactory::GetBrowserContextToUse(
428    content::BrowserContext* context) const {
429  return chrome::GetBrowserContextRedirectedInIncognito(context);
430}
431
432void 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