• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/strings/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_management.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/external_component_loader.h"
24 #include "chrome/browser/extensions/external_policy_loader.h"
25 #include "chrome/browser/extensions/external_pref_loader.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
30 #include "components/crx_file/id_util.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "extensions/browser/extension_system.h"
33 #include "extensions/browser/external_provider_interface.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/manifest.h"
36 #include "ui/base/l10n/l10n_util.h"
37 
38 #if defined(OS_CHROMEOS)
39 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
40 #include "chrome/browser/chromeos/customization_document.h"
41 #include "chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h"
42 #include "chrome/browser/chromeos/policy/app_pack_updater.h"
43 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
44 #include "chrome/browser/chromeos/policy/device_local_account.h"
45 #include "chrome/browser/chromeos/policy/device_local_account_policy_service.h"
46 #include "chrome/browser/chromeos/profiles/profile_helper.h"
47 #include "components/user_manager/user.h"
48 #include "components/user_manager/user_manager.h"
49 #else
50 #include "chrome/browser/extensions/default_apps.h"
51 #endif
52 
53 #if defined(OS_WIN)
54 #include "chrome/browser/extensions/external_registry_loader_win.h"
55 #endif
56 
57 using content::BrowserThread;
58 
59 namespace extensions {
60 
61 // Constants for keeping track of extension preferences in a dictionary.
62 const char ExternalProviderImpl::kInstallParam[] = "install_parameter";
63 const char ExternalProviderImpl::kExternalCrx[] = "external_crx";
64 const char ExternalProviderImpl::kExternalVersion[] = "external_version";
65 const char ExternalProviderImpl::kExternalUpdateUrl[] = "external_update_url";
66 const char ExternalProviderImpl::kIsBookmarkApp[] = "is_bookmark_app";
67 const char ExternalProviderImpl::kIsFromWebstore[] = "is_from_webstore";
68 const char ExternalProviderImpl::kKeepIfPresent[] = "keep_if_present";
69 const char ExternalProviderImpl::kWasInstalledByOem[] = "was_installed_by_oem";
70 const char ExternalProviderImpl::kSupportedLocales[] = "supported_locales";
71 const char ExternalProviderImpl::kMayBeUntrusted[] = "may_be_untrusted";
72 
ExternalProviderImpl(VisitorInterface * service,const scoped_refptr<ExternalLoader> & loader,Profile * profile,Manifest::Location crx_location,Manifest::Location download_location,int creation_flags)73 ExternalProviderImpl::ExternalProviderImpl(
74     VisitorInterface* service,
75     const scoped_refptr<ExternalLoader>& loader,
76     Profile* profile,
77     Manifest::Location crx_location,
78     Manifest::Location download_location,
79     int creation_flags)
80     : crx_location_(crx_location),
81       download_location_(download_location),
82       service_(service),
83       ready_(false),
84       loader_(loader),
85       profile_(profile),
86       creation_flags_(creation_flags),
87       auto_acknowledge_(false) {
88   loader_->Init(this);
89 }
90 
~ExternalProviderImpl()91 ExternalProviderImpl::~ExternalProviderImpl() {
92   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
93   loader_->OwnerShutdown();
94 }
95 
VisitRegisteredExtension()96 void ExternalProviderImpl::VisitRegisteredExtension() {
97   // The loader will call back to SetPrefs.
98   loader_->StartLoading();
99 }
100 
SetPrefs(base::DictionaryValue * prefs)101 void ExternalProviderImpl::SetPrefs(base::DictionaryValue* prefs) {
102   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
103 
104   // Check if the service is still alive. It is possible that it went
105   // away while |loader_| was working on the FILE thread.
106   if (!service_) return;
107 
108   prefs_.reset(prefs);
109   ready_ = true;  // Queries for extensions are allowed from this point.
110 
111   // Set of unsupported extensions that need to be deleted from prefs_.
112   std::set<std::string> unsupported_extensions;
113 
114   // Notify ExtensionService about all the extensions this provider has.
115   for (base::DictionaryValue::Iterator i(*prefs_); !i.IsAtEnd(); i.Advance()) {
116     const std::string& extension_id = i.key();
117     const base::DictionaryValue* extension = NULL;
118 
119     if (!crx_file::id_util::IdIsValid(extension_id)) {
120       LOG(WARNING) << "Malformed extension dictionary: key "
121                    << extension_id.c_str() << " is not a valid id.";
122       continue;
123     }
124 
125     if (!i.value().GetAsDictionary(&extension)) {
126       LOG(WARNING) << "Malformed extension dictionary: key "
127                    << extension_id.c_str()
128                    << " has a value that is not a dictionary.";
129       continue;
130     }
131 
132     base::FilePath::StringType external_crx;
133     const base::Value* external_version_value = NULL;
134     std::string external_version;
135     std::string external_update_url;
136 
137     bool has_external_crx = extension->GetString(kExternalCrx, &external_crx);
138 
139     bool has_external_version = false;
140     if (extension->Get(kExternalVersion, &external_version_value)) {
141       if (external_version_value->IsType(base::Value::TYPE_STRING)) {
142         external_version_value->GetAsString(&external_version);
143         has_external_version = true;
144       } else {
145         LOG(WARNING) << "Malformed extension dictionary for extension: "
146                      << extension_id.c_str() << ". " << kExternalVersion
147                      << " value must be a string.";
148         continue;
149       }
150     }
151 
152     bool has_external_update_url = extension->GetString(kExternalUpdateUrl,
153                                                         &external_update_url);
154     if (has_external_crx != has_external_version) {
155       LOG(WARNING) << "Malformed extension dictionary for extension: "
156                    << extension_id.c_str() << ".  " << kExternalCrx
157                    << " and " << kExternalVersion << " must be used together.";
158       continue;
159     }
160 
161     if (has_external_crx == has_external_update_url) {
162       LOG(WARNING) << "Malformed extension dictionary for extension: "
163                    << extension_id.c_str() << ".  Exactly one of the "
164                    << "followng keys should be used: " << kExternalCrx
165                    << ", " << kExternalUpdateUrl << ".";
166       continue;
167     }
168 
169     // Check that extension supports current browser locale.
170     const base::ListValue* supported_locales = NULL;
171     if (extension->GetList(kSupportedLocales, &supported_locales)) {
172       std::vector<std::string> browser_locales;
173       l10n_util::GetParentLocales(g_browser_process->GetApplicationLocale(),
174                                   &browser_locales);
175 
176       size_t num_locales = supported_locales->GetSize();
177       bool locale_supported = false;
178       for (size_t j = 0; j < num_locales; j++) {
179         std::string current_locale;
180         if (supported_locales->GetString(j, &current_locale) &&
181             l10n_util::IsValidLocaleSyntax(current_locale)) {
182           current_locale = l10n_util::NormalizeLocale(current_locale);
183           if (std::find(browser_locales.begin(), browser_locales.end(),
184                         current_locale) != browser_locales.end()) {
185             locale_supported = true;
186             break;
187           }
188         } else {
189           LOG(WARNING) << "Unrecognized locale '" << current_locale
190                        << "' found as supported locale for extension: "
191                        << extension_id;
192         }
193       }
194 
195       if (!locale_supported) {
196         unsupported_extensions.insert(extension_id);
197         VLOG(1) << "Skip installing (or uninstall) external extension: "
198                 << extension_id << " because the extension doesn't support "
199                 << "the browser locale.";
200         continue;
201       }
202     }
203 
204     int creation_flags = creation_flags_;
205     bool is_bookmark_app;
206     if (extension->GetBoolean(kIsBookmarkApp, &is_bookmark_app) &&
207         is_bookmark_app) {
208       creation_flags |= Extension::FROM_BOOKMARK;
209     }
210     bool is_from_webstore = false;
211     if (extension->GetBoolean(kIsFromWebstore, &is_from_webstore) &&
212         is_from_webstore) {
213       creation_flags |= Extension::FROM_WEBSTORE;
214     }
215     bool keep_if_present = false;
216     if (extension->GetBoolean(kKeepIfPresent, &keep_if_present) &&
217         keep_if_present && profile_) {
218       ExtensionServiceInterface* extension_service =
219           ExtensionSystem::Get(profile_)->extension_service();
220       const Extension* extension = extension_service ?
221           extension_service->GetExtensionById(extension_id, true) : NULL;
222       if (!extension) {
223         VLOG(1) << "Skip installing (or uninstall) external extension: "
224                 << extension_id << " because the extension should be kept "
225                 << "only if it is already installed.";
226         continue;
227       }
228     }
229     bool was_installed_by_oem = false;
230     if (extension->GetBoolean(kWasInstalledByOem, &was_installed_by_oem) &&
231         was_installed_by_oem) {
232       creation_flags |= Extension::WAS_INSTALLED_BY_OEM;
233     }
234     bool may_be_untrusted = false;
235     if (extension->GetBoolean(kMayBeUntrusted, &may_be_untrusted) &&
236         may_be_untrusted) {
237       creation_flags |= Extension::MAY_BE_UNTRUSTED;
238     }
239 
240     std::string install_parameter;
241     extension->GetString(kInstallParam, &install_parameter);
242 
243     if (has_external_crx) {
244       if (crx_location_ == Manifest::INVALID_LOCATION) {
245         LOG(WARNING) << "This provider does not support installing external "
246                      << "extensions from crx files.";
247         continue;
248       }
249       if (external_crx.find(base::FilePath::kParentDirectory) !=
250           base::StringPiece::npos) {
251         LOG(WARNING) << "Path traversal not allowed in path: "
252                      << external_crx.c_str();
253         continue;
254       }
255 
256       // If the path is relative, and the provider has a base path,
257       // build the absolute path to the crx file.
258       base::FilePath path(external_crx);
259       if (!path.IsAbsolute()) {
260         base::FilePath base_path = loader_->GetBaseCrxFilePath();
261         if (base_path.empty()) {
262           LOG(WARNING) << "File path " << external_crx.c_str()
263                        << " is relative.  An absolute path is required.";
264           continue;
265         }
266         path = base_path.Append(external_crx);
267       }
268 
269       Version version(external_version);
270       if (!version.IsValid()) {
271         LOG(WARNING) << "Malformed extension dictionary for extension: "
272                      << extension_id.c_str() << ".  Invalid version string \""
273                      << external_version << "\".";
274         continue;
275       }
276       service_->OnExternalExtensionFileFound(extension_id, &version, path,
277                                              crx_location_, creation_flags,
278                                              auto_acknowledge_);
279     } else {  // if (has_external_update_url)
280       CHECK(has_external_update_url);  // Checking of keys above ensures this.
281       if (download_location_ == Manifest::INVALID_LOCATION) {
282         LOG(WARNING) << "This provider does not support installing external "
283                      << "extensions from update URLs.";
284         continue;
285       }
286       GURL update_url(external_update_url);
287       if (!update_url.is_valid()) {
288         LOG(WARNING) << "Malformed extension dictionary for extension: "
289                      << extension_id.c_str() << ".  Key " << kExternalUpdateUrl
290                      << " has value \"" << external_update_url
291                      << "\", which is not a valid URL.";
292         continue;
293       }
294       service_->OnExternalExtensionUpdateUrlFound(extension_id,
295                                                   install_parameter,
296                                                   update_url,
297                                                   download_location_,
298                                                   creation_flags,
299                                                   auto_acknowledge_);
300     }
301   }
302 
303   for (std::set<std::string>::iterator it = unsupported_extensions.begin();
304        it != unsupported_extensions.end(); ++it) {
305     // Remove extension for the list of know external extensions. The extension
306     // will be uninstalled later because provider doesn't provide it anymore.
307     prefs_->Remove(*it, NULL);
308   }
309 
310   service_->OnExternalProviderReady(this);
311 }
312 
ServiceShutdown()313 void ExternalProviderImpl::ServiceShutdown() {
314   service_ = NULL;
315 }
316 
IsReady() const317 bool ExternalProviderImpl::IsReady() const {
318   return ready_;
319 }
320 
HasExtension(const std::string & id) const321 bool ExternalProviderImpl::HasExtension(
322     const std::string& id) const {
323   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324   CHECK(prefs_.get());
325   CHECK(ready_);
326   return prefs_->HasKey(id);
327 }
328 
GetExtensionDetails(const std::string & id,Manifest::Location * location,scoped_ptr<Version> * version) const329 bool ExternalProviderImpl::GetExtensionDetails(
330     const std::string& id, Manifest::Location* location,
331     scoped_ptr<Version>* version) const {
332   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333   CHECK(prefs_.get());
334   CHECK(ready_);
335   base::DictionaryValue* extension = NULL;
336   if (!prefs_->GetDictionary(id, &extension))
337     return false;
338 
339   Manifest::Location loc = Manifest::INVALID_LOCATION;
340   if (extension->HasKey(kExternalUpdateUrl)) {
341     loc = download_location_;
342 
343   } else if (extension->HasKey(kExternalCrx)) {
344     loc = crx_location_;
345 
346     std::string external_version;
347     if (!extension->GetString(kExternalVersion, &external_version))
348       return false;
349 
350     if (version)
351       version->reset(new Version(external_version));
352 
353   } else {
354     NOTREACHED();  // Chrome should not allow prefs to get into this state.
355     return false;
356   }
357 
358   if (location)
359     *location = loc;
360 
361   return true;
362 }
363 
364 // static
CreateExternalProviders(VisitorInterface * service,Profile * profile,ProviderCollection * provider_list)365 void ExternalProviderImpl::CreateExternalProviders(
366     VisitorInterface* service,
367     Profile* profile,
368     ProviderCollection* provider_list) {
369   scoped_refptr<ExternalLoader> external_loader;
370   extensions::Manifest::Location crx_location = Manifest::INVALID_LOCATION;
371 #if defined(OS_CHROMEOS)
372   policy::BrowserPolicyConnectorChromeOS* connector =
373       g_browser_process->platform_part()->browser_policy_connector_chromeos();
374   bool is_chrome_os_public_session = false;
375   const user_manager::User* user =
376       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
377   policy::DeviceLocalAccount::Type account_type;
378   if (user && policy::IsDeviceLocalAccountUser(user->email(), &account_type)) {
379     if (account_type == policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION)
380       is_chrome_os_public_session = true;
381     policy::DeviceLocalAccountPolicyBroker* broker =
382         connector->GetDeviceLocalAccountPolicyService()->GetBrokerForUser(
383             user->email());
384     if (broker) {
385       external_loader = broker->extension_loader();
386       crx_location = Manifest::EXTERNAL_POLICY;
387     } else {
388       NOTREACHED();
389     }
390   } else {
391     external_loader = new ExternalPolicyLoader(
392         ExtensionManagementFactory::GetForBrowserContext(profile));
393   }
394 #else
395   external_loader = new ExternalPolicyLoader(
396       ExtensionManagementFactory::GetForBrowserContext(profile));
397 #endif
398 
399   // Policies are mandatory so they can't be skipped with command line flag.
400   if (external_loader.get()) {
401     provider_list->push_back(
402         linked_ptr<ExternalProviderInterface>(
403             new ExternalProviderImpl(
404                 service,
405                 external_loader,
406                 profile,
407                 crx_location,
408                 Manifest::EXTERNAL_POLICY_DOWNLOAD,
409                 Extension::NO_FLAGS)));
410   }
411 
412   // Load the KioskAppExternalProvider when running in kiosk mode.
413   if (chrome::IsRunningInForcedAppMode()) {
414 #if defined(OS_CHROMEOS)
415     chromeos::KioskAppManager* kiosk_app_manager =
416         chromeos::KioskAppManager::Get();
417     DCHECK(kiosk_app_manager);
418     if (kiosk_app_manager && !kiosk_app_manager->external_loader_created()) {
419       provider_list->push_back(linked_ptr<ExternalProviderInterface>(
420           new ExternalProviderImpl(service,
421                                    kiosk_app_manager->CreateExternalLoader(),
422                                    profile,
423                                    Manifest::EXTERNAL_PREF,
424                                    Manifest::INVALID_LOCATION,
425                                    Extension::NO_FLAGS)));
426     }
427 #endif
428     return;
429   }
430 
431   // In tests don't install extensions from default external sources.
432   // It would only slowdown tests and make them flaky.
433   if (CommandLine::ForCurrentProcess()->HasSwitch(
434       switches::kDisableDefaultApps))
435     return;
436 
437   // On Mac OS, items in /Library/... should be written by the superuser.
438   // Check that all components of the path are writable by root only.
439   ExternalPrefLoader::Options check_admin_permissions_on_mac;
440 #if defined(OS_MACOSX)
441   check_admin_permissions_on_mac =
442     ExternalPrefLoader::ENSURE_PATH_CONTROLLED_BY_ADMIN;
443 #else
444   check_admin_permissions_on_mac = ExternalPrefLoader::NONE;
445 #endif
446 
447   bool is_chromeos_demo_session = false;
448   int bundled_extension_creation_flags = Extension::NO_FLAGS;
449 #if defined(OS_CHROMEOS)
450   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
451   is_chromeos_demo_session =
452       user_manager && user_manager->IsLoggedInAsDemoUser() &&
453       connector->GetDeviceMode() == policy::DEVICE_MODE_RETAIL_KIOSK;
454   bundled_extension_creation_flags = Extension::FROM_WEBSTORE |
455       Extension::WAS_INSTALLED_BY_DEFAULT;
456 #endif
457 
458 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
459   if (!profile->IsSupervised()) {
460     provider_list->push_back(
461         linked_ptr<ExternalProviderInterface>(
462             new ExternalProviderImpl(
463                 service,
464                 new ExternalPrefLoader(
465                     chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
466                     ExternalPrefLoader::NONE),
467                 profile,
468                 Manifest::EXTERNAL_PREF,
469                 Manifest::EXTERNAL_PREF_DOWNLOAD,
470                 bundled_extension_creation_flags)));
471   }
472 #endif
473 
474 #if defined(OS_CHROMEOS)
475   if (!is_chromeos_demo_session && !is_chrome_os_public_session) {
476     int external_apps_path_id = profile->IsSupervised() ?
477         chrome::DIR_SUPERVISED_USERS_DEFAULT_APPS :
478         chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS;
479     provider_list->push_back(
480         linked_ptr<ExternalProviderInterface>(new ExternalProviderImpl(
481             service,
482             new ExternalPrefLoader(external_apps_path_id,
483                                    ExternalPrefLoader::NONE),
484             profile,
485             Manifest::EXTERNAL_PREF,
486             Manifest::EXTERNAL_PREF_DOWNLOAD,
487             bundled_extension_creation_flags)));
488 
489     // OEM default apps.
490     int oem_extension_creation_flags =
491         bundled_extension_creation_flags | Extension::WAS_INSTALLED_BY_OEM;
492     chromeos::ServicesCustomizationDocument* customization =
493         chromeos::ServicesCustomizationDocument::GetInstance();
494     provider_list->push_back(linked_ptr<ExternalProviderInterface>(
495         new ExternalProviderImpl(service,
496                                  customization->CreateExternalLoader(profile),
497                                  profile,
498                                  Manifest::EXTERNAL_PREF,
499                                  Manifest::EXTERNAL_PREF_DOWNLOAD,
500                                  oem_extension_creation_flags)));
501   }
502 
503   policy::AppPackUpdater* app_pack_updater = connector->GetAppPackUpdater();
504   if (is_chromeos_demo_session && app_pack_updater &&
505       !app_pack_updater->created_external_loader()) {
506     provider_list->push_back(
507         linked_ptr<ExternalProviderInterface>(
508           new ExternalProviderImpl(
509               service,
510               app_pack_updater->CreateExternalLoader(),
511               profile,
512               Manifest::EXTERNAL_PREF,
513               Manifest::INVALID_LOCATION,
514               Extension::NO_FLAGS)));
515   }
516 #endif
517 
518   if (!profile->IsSupervised() && !is_chromeos_demo_session) {
519 #if !defined(OS_WIN)
520     provider_list->push_back(
521         linked_ptr<ExternalProviderInterface>(
522             new ExternalProviderImpl(
523                 service,
524                 new ExternalPrefLoader(chrome::DIR_EXTERNAL_EXTENSIONS,
525                                        check_admin_permissions_on_mac),
526                 profile,
527                 Manifest::EXTERNAL_PREF,
528                 Manifest::EXTERNAL_PREF_DOWNLOAD,
529                 bundled_extension_creation_flags)));
530 #endif
531 
532     // Define a per-user source of external extensions.
533 #if defined(OS_MACOSX)
534     provider_list->push_back(
535         linked_ptr<ExternalProviderInterface>(
536             new ExternalProviderImpl(
537                 service,
538                 new ExternalPrefLoader(chrome::DIR_USER_EXTERNAL_EXTENSIONS,
539                                        ExternalPrefLoader::NONE),
540                 profile,
541                 Manifest::EXTERNAL_PREF,
542                 Manifest::EXTERNAL_PREF_DOWNLOAD,
543                 Extension::NO_FLAGS)));
544 #endif
545 
546 #if defined(OS_WIN)
547     provider_list->push_back(
548         linked_ptr<ExternalProviderInterface>(
549             new ExternalProviderImpl(
550                 service,
551                 new ExternalRegistryLoader,
552                 profile,
553                 Manifest::EXTERNAL_REGISTRY,
554                 Manifest::EXTERNAL_PREF_DOWNLOAD,
555                 Extension::NO_FLAGS)));
556 #endif
557 
558 #if !defined(OS_CHROMEOS)
559     // The default apps are installed as INTERNAL but use the external
560     // extension installer codeflow.
561     provider_list->push_back(
562         linked_ptr<ExternalProviderInterface>(
563             new default_apps::Provider(
564                 profile,
565                 service,
566                 new ExternalPrefLoader(chrome::DIR_DEFAULT_APPS,
567                                        ExternalPrefLoader::NONE),
568                 Manifest::INTERNAL,
569                 Manifest::INTERNAL,
570                 Extension::FROM_WEBSTORE |
571                     Extension::WAS_INSTALLED_BY_DEFAULT)));
572 #endif
573 
574     provider_list->push_back(
575       linked_ptr<ExternalProviderInterface>(
576         new ExternalProviderImpl(
577             service,
578             new ExternalComponentLoader(profile),
579             profile,
580             Manifest::INVALID_LOCATION,
581             Manifest::EXTERNAL_COMPONENT,
582             Extension::FROM_WEBSTORE | Extension::WAS_INSTALLED_BY_DEFAULT)));
583   }
584 }
585 
586 }  // namespace extensions
587