external_provider_impl.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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/external_provider_impl.h" 6 7#include <set> 8#include <vector> 9 10#include "base/command_line.h" 11#include "base/files/file_path.h" 12#include "base/logging.h" 13#include "base/memory/linked_ptr.h" 14#include "base/metrics/field_trial.h" 15#include "base/path_service.h" 16#include "base/string_util.h" 17#include "base/values.h" 18#include "base/version.h" 19#include "chrome/browser/app_mode/app_mode_utils.h" 20#include "chrome/browser/browser_process.h" 21#include "chrome/browser/extensions/extension_service.h" 22#include "chrome/browser/extensions/external_policy_loader.h" 23#include "chrome/browser/extensions/external_pref_loader.h" 24#include "chrome/browser/extensions/external_provider_interface.h" 25#include "chrome/browser/profiles/profile.h" 26#include "chrome/common/chrome_paths.h" 27#include "chrome/common/chrome_switches.h" 28#include "chrome/common/extensions/extension.h" 29#include "chrome/common/extensions/manifest.h" 30#include "chrome/common/pref_names.h" 31#include "content/public/browser/browser_thread.h" 32#include "ui/base/l10n/l10n_util.h" 33 34#if defined(ENABLE_MANAGED_USERS) 35#include "chrome/browser/managed_mode/managed_user_service.h" 36#include "chrome/browser/managed_mode/managed_user_service_factory.h" 37#endif 38 39#if defined(OS_CHROMEOS) 40#include "chrome/browser/chromeos/login/user_manager.h" 41#include "chrome/browser/chromeos/policy/app_pack_updater.h" 42#include "chrome/browser/policy/browser_policy_connector.h" 43#else 44#include "chrome/browser/extensions/default_apps.h" 45#endif 46 47#if defined(OS_WIN) 48#include "chrome/browser/extensions/external_registry_loader_win.h" 49#endif 50 51using content::BrowserThread; 52 53namespace extensions { 54 55// Constants for keeping track of extension preferences in a dictionary. 56const char ExternalProviderImpl::kExternalCrx[] = "external_crx"; 57const char ExternalProviderImpl::kExternalVersion[] = "external_version"; 58const char ExternalProviderImpl::kExternalUpdateUrl[] = "external_update_url"; 59const char ExternalProviderImpl::kSupportedLocales[] = "supported_locales"; 60const char ExternalProviderImpl::kIsBookmarkApp[] = "is_bookmark_app"; 61const char ExternalProviderImpl::kIsFromWebstore[] = "is_from_webstore"; 62 63ExternalProviderImpl::ExternalProviderImpl( 64 VisitorInterface* service, 65 ExternalLoader* loader, 66 Manifest::Location crx_location, 67 Manifest::Location download_location, 68 int creation_flags) 69 : crx_location_(crx_location), 70 download_location_(download_location), 71 service_(service), 72 prefs_(NULL), 73 ready_(false), 74 loader_(loader), 75 creation_flags_(creation_flags), 76 auto_acknowledge_(false) { 77 loader_->Init(this); 78} 79 80ExternalProviderImpl::~ExternalProviderImpl() { 81 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 82 loader_->OwnerShutdown(); 83} 84 85void ExternalProviderImpl::VisitRegisteredExtension() { 86 // The loader will call back to SetPrefs. 87 loader_->StartLoading(); 88} 89 90void ExternalProviderImpl::SetPrefs(DictionaryValue* prefs) { 91 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 92 93 // Check if the service is still alive. It is possible that it went 94 // away while |loader_| was working on the FILE thread. 95 if (!service_) return; 96 97 prefs_.reset(prefs); 98 ready_ = true; // Queries for extensions are allowed from this point. 99 100 // Set of unsupported extensions that need to be deleted from prefs_. 101 std::set<std::string> unsupported_extensions; 102 103 // Notify ExtensionService about all the extensions this provider has. 104 for (DictionaryValue::Iterator i(*prefs_); !i.IsAtEnd(); i.Advance()) { 105 const std::string& extension_id = i.key(); 106 const DictionaryValue* extension = NULL; 107 108 if (!Extension::IdIsValid(extension_id)) { 109 LOG(WARNING) << "Malformed extension dictionary: key " 110 << extension_id.c_str() << " is not a valid id."; 111 continue; 112 } 113 114 if (!i.value().GetAsDictionary(&extension)) { 115 LOG(WARNING) << "Malformed extension dictionary: key " 116 << extension_id.c_str() 117 << " has a value that is not a dictionary."; 118 continue; 119 } 120 121 base::FilePath::StringType external_crx; 122 const Value* external_version_value = NULL; 123 std::string external_version; 124 std::string external_update_url; 125 126 bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); 127 128 bool has_external_version = false; 129 if (extension->Get(kExternalVersion, &external_version_value)) { 130 if (external_version_value->IsType(Value::TYPE_STRING)) { 131 external_version_value->GetAsString(&external_version); 132 has_external_version = true; 133 } else { 134 LOG(WARNING) << "Malformed extension dictionary for extension: " 135 << extension_id.c_str() << ". " << kExternalVersion 136 << " value must be a string."; 137 continue; 138 } 139 } 140 141 bool has_external_update_url = extension->GetString(kExternalUpdateUrl, 142 &external_update_url); 143 if (has_external_crx != has_external_version) { 144 LOG(WARNING) << "Malformed extension dictionary for extension: " 145 << extension_id.c_str() << ". " << kExternalCrx 146 << " and " << kExternalVersion << " must be used together."; 147 continue; 148 } 149 150 if (has_external_crx == has_external_update_url) { 151 LOG(WARNING) << "Malformed extension dictionary for extension: " 152 << extension_id.c_str() << ". Exactly one of the " 153 << "followng keys should be used: " << kExternalCrx 154 << ", " << kExternalUpdateUrl << "."; 155 continue; 156 } 157 158 // Check that extension supports current browser locale. 159 const ListValue* supported_locales = NULL; 160 if (extension->GetList(kSupportedLocales, &supported_locales)) { 161 std::vector<std::string> browser_locales; 162 l10n_util::GetParentLocales(g_browser_process->GetApplicationLocale(), 163 &browser_locales); 164 165 size_t num_locales = supported_locales->GetSize(); 166 bool locale_supported = false; 167 for (size_t j = 0; j < num_locales; j++) { 168 std::string current_locale; 169 if (supported_locales->GetString(j, ¤t_locale) && 170 l10n_util::IsValidLocaleSyntax(current_locale)) { 171 current_locale = l10n_util::NormalizeLocale(current_locale); 172 if (std::find(browser_locales.begin(), browser_locales.end(), 173 current_locale) != browser_locales.end()) { 174 locale_supported = true; 175 break; 176 } 177 } else { 178 LOG(WARNING) << "Unrecognized locale '" << current_locale 179 << "' found as supported locale for extension: " 180 << extension_id; 181 } 182 } 183 184 if (!locale_supported) { 185 unsupported_extensions.insert(extension_id); 186 LOG(INFO) << "Skip installing (or uninstall) external extension: " 187 << extension_id << " because the extension doesn't support " 188 << "the browser locale."; 189 continue; 190 } 191 } 192 193 int creation_flags = creation_flags_; 194 bool is_bookmark_app; 195 if (extension->GetBoolean(kIsBookmarkApp, &is_bookmark_app) && 196 is_bookmark_app) { 197 creation_flags |= Extension::FROM_BOOKMARK; 198 } 199 bool is_from_webstore; 200 if (extension->GetBoolean(kIsFromWebstore, &is_from_webstore) && 201 is_from_webstore) { 202 creation_flags |= Extension::FROM_WEBSTORE; 203 } 204 205 if (has_external_crx) { 206 if (crx_location_ == Manifest::INVALID_LOCATION) { 207 LOG(WARNING) << "This provider does not support installing external " 208 << "extensions from crx files."; 209 continue; 210 } 211 if (external_crx.find(base::FilePath::kParentDirectory) != 212 base::StringPiece::npos) { 213 LOG(WARNING) << "Path traversal not allowed in path: " 214 << external_crx.c_str(); 215 continue; 216 } 217 218 // If the path is relative, and the provider has a base path, 219 // build the absolute path to the crx file. 220 base::FilePath path(external_crx); 221 if (!path.IsAbsolute()) { 222 base::FilePath base_path = loader_->GetBaseCrxFilePath(); 223 if (base_path.empty()) { 224 LOG(WARNING) << "File path " << external_crx.c_str() 225 << " is relative. An absolute path is required."; 226 continue; 227 } 228 path = base_path.Append(external_crx); 229 } 230 231 Version version(external_version); 232 if (!version.IsValid()) { 233 LOG(WARNING) << "Malformed extension dictionary for extension: " 234 << extension_id.c_str() << ". Invalid version string \"" 235 << external_version << "\"."; 236 continue; 237 } 238 service_->OnExternalExtensionFileFound(extension_id, &version, path, 239 crx_location_, creation_flags, 240 auto_acknowledge_); 241 } else { // if (has_external_update_url) 242 CHECK(has_external_update_url); // Checking of keys above ensures this. 243 if (download_location_ == Manifest::INVALID_LOCATION) { 244 LOG(WARNING) << "This provider does not support installing external " 245 << "extensions from update URLs."; 246 continue; 247 } 248 GURL update_url(external_update_url); 249 if (!update_url.is_valid()) { 250 LOG(WARNING) << "Malformed extension dictionary for extension: " 251 << extension_id.c_str() << ". Key " << kExternalUpdateUrl 252 << " has value \"" << external_update_url 253 << "\", which is not a valid URL."; 254 continue; 255 } 256 service_->OnExternalExtensionUpdateUrlFound( 257 extension_id, update_url, download_location_); 258 } 259 } 260 261 for (std::set<std::string>::iterator it = unsupported_extensions.begin(); 262 it != unsupported_extensions.end(); ++it) { 263 // Remove extension for the list of know external extensions. The extension 264 // will be uninstalled later because provider doesn't provide it anymore. 265 prefs_->Remove(*it, NULL); 266 } 267 268 service_->OnExternalProviderReady(this); 269} 270 271void ExternalProviderImpl::ServiceShutdown() { 272 service_ = NULL; 273} 274 275bool ExternalProviderImpl::IsReady() const { 276 return ready_; 277} 278 279bool ExternalProviderImpl::HasExtension( 280 const std::string& id) const { 281 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 282 CHECK(prefs_.get()); 283 CHECK(ready_); 284 return prefs_->HasKey(id); 285} 286 287bool ExternalProviderImpl::GetExtensionDetails( 288 const std::string& id, Manifest::Location* location, 289 scoped_ptr<Version>* version) const { 290 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 291 CHECK(prefs_.get()); 292 CHECK(ready_); 293 DictionaryValue* extension = NULL; 294 if (!prefs_->GetDictionary(id, &extension)) 295 return false; 296 297 Manifest::Location loc = Manifest::INVALID_LOCATION; 298 if (extension->HasKey(kExternalUpdateUrl)) { 299 loc = download_location_; 300 301 } else if (extension->HasKey(kExternalCrx)) { 302 loc = crx_location_; 303 304 std::string external_version; 305 if (!extension->GetString(kExternalVersion, &external_version)) 306 return false; 307 308 if (version) 309 version->reset(new Version(external_version)); 310 311 } else { 312 NOTREACHED(); // Chrome should not allow prefs to get into this state. 313 return false; 314 } 315 316 if (location) 317 *location = loc; 318 319 return true; 320} 321 322// static 323void ExternalProviderImpl::CreateExternalProviders( 324 VisitorInterface* service, 325 Profile* profile, 326 ProviderCollection* provider_list) { 327 // Policies are mandatory so they can't be skipped with command line flag. 328 provider_list->push_back( 329 linked_ptr<ExternalProviderInterface>( 330 new ExternalProviderImpl( 331 service, 332 new ExternalPolicyLoader(profile), 333 Manifest::INVALID_LOCATION, 334 Manifest::EXTERNAL_POLICY_DOWNLOAD, 335 Extension::NO_FLAGS))); 336 337 // In tests don't install extensions from default external sources. 338 // It would only slowdown tests and make them flaky. 339 if (CommandLine::ForCurrentProcess()->HasSwitch( 340 switches::kDisableDefaultApps)) 341 return; 342 343 // No external app install in app mode. 344 if (chrome::IsRunningInForcedAppMode()) 345 return; 346 347 // On Mac OS, items in /Library/... should be written by the superuser. 348 // Check that all components of the path are writable by root only. 349 ExternalPrefLoader::Options check_admin_permissions_on_mac; 350#if defined(OS_MACOSX) 351 check_admin_permissions_on_mac = 352 ExternalPrefLoader::ENSURE_PATH_CONTROLLED_BY_ADMIN; 353#else 354 check_admin_permissions_on_mac = ExternalPrefLoader::NONE; 355#endif 356 357 bool is_chromeos_demo_session = false; 358 int bundled_extension_creation_flags = Extension::NO_FLAGS; 359#if defined(OS_CHROMEOS) 360 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); 361 is_chromeos_demo_session = 362 user_manager && user_manager->IsLoggedInAsDemoUser() && 363 g_browser_process->browser_policy_connector()->GetDeviceMode() == 364 policy::DEVICE_MODE_KIOSK; 365 bundled_extension_creation_flags = Extension::FROM_WEBSTORE | 366 Extension::WAS_INSTALLED_BY_DEFAULT; 367#endif 368 369 bool is_managed_profile = false; 370 int external_apps_path_id = chrome::DIR_EXTERNAL_EXTENSIONS; 371#if defined(ENABLE_MANAGED_USERS) 372 ManagedUserService* managed_user_service = 373 ManagedUserServiceFactory::GetForProfile(profile); 374 is_managed_profile = managed_user_service->ProfileIsManaged(); 375 if (is_managed_profile) 376 external_apps_path_id = chrome::DIR_MANAGED_USERS_DEFAULT_APPS; 377#endif 378 379 if (!is_chromeos_demo_session) { 380 provider_list->push_back( 381 linked_ptr<ExternalProviderInterface>( 382 new ExternalProviderImpl( 383 service, 384 new ExternalPrefLoader(external_apps_path_id, 385 check_admin_permissions_on_mac), 386 Manifest::EXTERNAL_PREF, 387 Manifest::EXTERNAL_PREF_DOWNLOAD, 388 bundled_extension_creation_flags))); 389 } 390 391 if (!is_managed_profile) { 392#if defined(OS_CHROMEOS) || defined (OS_MACOSX) 393 // Define a per-user source of external extensions. 394 // On Chrome OS, this serves as a source for OEM customization. 395 provider_list->push_back( 396 linked_ptr<ExternalProviderInterface>( 397 new ExternalProviderImpl( 398 service, 399 new ExternalPrefLoader(chrome::DIR_USER_EXTERNAL_EXTENSIONS, 400 ExternalPrefLoader::NONE), 401 Manifest::EXTERNAL_PREF, 402 Manifest::EXTERNAL_PREF_DOWNLOAD, 403 Extension::NO_FLAGS))); 404#endif 405 406#if defined(OS_WIN) 407 provider_list->push_back( 408 linked_ptr<ExternalProviderInterface>( 409 new ExternalProviderImpl( 410 service, 411 new ExternalRegistryLoader, 412 Manifest::EXTERNAL_REGISTRY, 413 Manifest::INVALID_LOCATION, 414 Extension::NO_FLAGS))); 415#endif 416 417#if defined(OS_LINUX) 418 provider_list->push_back( 419 linked_ptr<ExternalProviderInterface>( 420 new ExternalProviderImpl( 421 service, 422 new ExternalPrefLoader( 423 chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS, 424 ExternalPrefLoader::NONE), 425 Manifest::EXTERNAL_PREF, 426 Manifest::EXTERNAL_PREF_DOWNLOAD, 427 bundled_extension_creation_flags))); 428#endif 429 430#if !defined(OS_CHROMEOS) 431 // The default apps are installed as INTERNAL but use the external 432 // extension installer codeflow. 433 provider_list->push_back( 434 linked_ptr<ExternalProviderInterface>( 435 new default_apps::Provider( 436 profile, 437 service, 438 new ExternalPrefLoader(chrome::DIR_DEFAULT_APPS, 439 ExternalPrefLoader::NONE), 440 Manifest::INTERNAL, 441 Manifest::INVALID_LOCATION, 442 Extension::FROM_WEBSTORE | 443 Extension::WAS_INSTALLED_BY_DEFAULT))); 444#endif 445 446#if defined(OS_CHROMEOS) 447 policy::BrowserPolicyConnector* connector = 448 g_browser_process->browser_policy_connector(); 449 if (is_chromeos_demo_session && connector->GetAppPackUpdater()) { 450 provider_list->push_back( 451 linked_ptr<ExternalProviderInterface>( 452 new ExternalProviderImpl( 453 service, 454 connector->GetAppPackUpdater()->CreateExternalLoader(), 455 Manifest::EXTERNAL_PREF, 456 Manifest::INVALID_LOCATION, 457 Extension::NO_FLAGS))); 458 } 459#endif 460 } 461} 462 463} // namespace extensions 464