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