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