1 // Copyright (c) 2013 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/unpacked_installer.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/files/file_util.h"
12 #include "base/path_service.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "build/branding_buildflags.h"
17 #include "chrome/browser/extensions/extension_management.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/load_error_reporter.h"
20 #include "chrome/browser/extensions/permissions_updater.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "components/crx_file/id_util.h"
23 #include "components/sync/model/string_ordinal.h"
24 #include "content/public/browser/browser_task_traits.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "extensions/browser/api/declarative_net_request/index_helper.h"
27 #include "extensions/browser/extension_file_task_runner.h"
28 #include "extensions/browser/extension_prefs.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/install_flag.h"
31 #include "extensions/browser/path_util.h"
32 #include "extensions/browser/policy_check.h"
33 #include "extensions/browser/preload_check_group.h"
34 #include "extensions/browser/requirements_checker.h"
35 #include "extensions/common/constants.h"
36 #include "extensions/common/extension.h"
37 #include "extensions/common/extension_l10n_util.h"
38 #include "extensions/common/file_util.h"
39 #include "extensions/common/manifest.h"
40 #include "extensions/common/manifest_constants.h"
41 #include "extensions/common/manifest_handlers/shared_module_info.h"
42 #include "extensions/common/permissions/permissions_data.h"
43 
44 using content::BrowserThread;
45 using extensions::Extension;
46 using extensions::SharedModuleInfo;
47 
48 namespace extensions {
49 
50 namespace {
51 
52 const char kUnpackedExtensionsBlocklistedError[] =
53     "Loading of unpacked extensions is disabled by the administrator.";
54 
55 const char kImportMinVersionNewer[] =
56     "'import' version requested is newer than what is installed.";
57 const char kImportMissing[] = "'import' extension is not installed.";
58 const char kImportNotSharedModule[] = "'import' is not a shared module.";
59 
60 // Deletes files reserved for use by the Extension system in the kMetadataFolder
61 // and the kMetadataFolder itself if it is empty.
MaybeCleanupMetadataFolder(const base::FilePath & extension_path)62 void MaybeCleanupMetadataFolder(const base::FilePath& extension_path) {
63   const std::vector<base::FilePath> reserved_filepaths =
64       file_util::GetReservedMetadataFilePaths(extension_path);
65   for (const auto& file : reserved_filepaths)
66     base::DeletePathRecursively(file);
67 
68   const base::FilePath& metadata_dir = extension_path.Append(kMetadataFolder);
69   if (base::IsDirectoryEmpty(metadata_dir))
70     base::DeletePathRecursively(metadata_dir);
71 }
72 
73 }  // namespace
74 
75 // static
Create(ExtensionService * extension_service)76 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
77     ExtensionService* extension_service) {
78   DCHECK(extension_service);
79   return scoped_refptr<UnpackedInstaller>(
80       new UnpackedInstaller(extension_service));
81 }
82 
UnpackedInstaller(ExtensionService * extension_service)83 UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
84     : service_weak_(extension_service->AsWeakPtr()),
85       profile_(extension_service->profile()),
86       require_modern_manifest_version_(true),
87       be_noisy_on_failure_(true) {
88   DCHECK_CURRENTLY_ON(BrowserThread::UI);
89 }
90 
~UnpackedInstaller()91 UnpackedInstaller::~UnpackedInstaller() {
92 }
93 
Load(const base::FilePath & path_in)94 void UnpackedInstaller::Load(const base::FilePath& path_in) {
95   DCHECK(extension_path_.empty());
96   extension_path_ = path_in;
97   GetExtensionFileTaskRunner()->PostTask(
98       FROM_HERE, base::BindOnce(&UnpackedInstaller::GetAbsolutePath, this));
99 }
100 
LoadFromCommandLine(const base::FilePath & path_in,std::string * extension_id,bool only_allow_apps)101 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
102                                             std::string* extension_id,
103                                             bool only_allow_apps) {
104   DCHECK_CURRENTLY_ON(BrowserThread::UI);
105   DCHECK(extension_path_.empty());
106 
107   if (!service_weak_.get())
108     return false;
109   // Load extensions from the command line synchronously to avoid a race
110   // between extension loading and loading an URL from the command line.
111   base::ThreadRestrictions::ScopedAllowIO allow_io;
112 
113   extension_path_ =
114       base::MakeAbsoluteFilePath(path_util::ResolveHomeDirectory(path_in));
115 
116   if (!IsLoadingUnpackedAllowed()) {
117     ReportExtensionLoadError(kUnpackedExtensionsBlocklistedError);
118     return false;
119   }
120 
121   std::string error;
122   if (!LoadExtension(Manifest::COMMAND_LINE, GetFlags(), &error)) {
123     ReportExtensionLoadError(error);
124     return false;
125   }
126 
127   if (only_allow_apps && !extension()->is_platform_app()) {
128 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
129     // Avoid crashing for users with hijacked shortcuts.
130     return true;
131 #else
132     // Defined here to avoid unused variable errors in official builds.
133     const char extension_instead_of_app_error[] =
134         "App loading flags cannot be used to load extensions. Please use "
135         "--load-extension instead.";
136     ReportExtensionLoadError(extension_instead_of_app_error);
137     return false;
138 #endif
139   }
140 
141   extension()->permissions_data()->BindToCurrentThread();
142   PermissionsUpdater(
143       service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT)
144       .InitializePermissions(extension());
145   StartInstallChecks();
146 
147   *extension_id = extension()->id();
148   return true;
149 }
150 
StartInstallChecks()151 void UnpackedInstaller::StartInstallChecks() {
152   DCHECK_CURRENTLY_ON(BrowserThread::UI);
153   ExtensionService* service = service_weak_.get();
154   if (!service)
155     return;
156 
157   // TODO(crbug.com/421128): Enable these checks all the time.  The reason
158   // they are disabled for extensions loaded from the command-line is that
159   // installing unpacked extensions is asynchronous, but there can be
160   // dependencies between the extensions loaded by the command line.
161   if (extension()->manifest()->location() != Manifest::COMMAND_LINE) {
162     if (service->browser_terminating())
163       return;
164 
165     // TODO(crbug.com/420147): Move this code to a utility class to avoid
166     // duplication of SharedModuleService::CheckImports code.
167     if (SharedModuleInfo::ImportsModules(extension())) {
168       const std::vector<SharedModuleInfo::ImportInfo>& imports =
169           SharedModuleInfo::GetImports(extension());
170       std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
171       ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
172       for (i = imports.begin(); i != imports.end(); ++i) {
173         base::Version version_required(i->minimum_version);
174         const Extension* imported_module = registry->GetExtensionById(
175             i->extension_id, ExtensionRegistry::EVERYTHING);
176         if (!imported_module) {
177           ReportExtensionLoadError(kImportMissing);
178           return;
179         } else if (imported_module &&
180                    !SharedModuleInfo::IsSharedModule(imported_module)) {
181           ReportExtensionLoadError(kImportNotSharedModule);
182           return;
183         } else if (imported_module && (version_required.IsValid() &&
184                                        imported_module->version().CompareTo(
185                                            version_required) < 0)) {
186           ReportExtensionLoadError(kImportMinVersionNewer);
187           return;
188         }
189       }
190     }
191   }
192 
193   policy_check_ = std::make_unique<PolicyCheck>(profile_, extension_);
194   requirements_check_ = std::make_unique<RequirementsChecker>(extension_);
195 
196   check_group_ = std::make_unique<PreloadCheckGroup>();
197   check_group_->set_stop_on_first_error(true);
198 
199   check_group_->AddCheck(policy_check_.get());
200   check_group_->AddCheck(requirements_check_.get());
201   check_group_->Start(
202       base::BindOnce(&UnpackedInstaller::OnInstallChecksComplete, this));
203 }
204 
OnInstallChecksComplete(const PreloadCheck::Errors & errors)205 void UnpackedInstaller::OnInstallChecksComplete(
206     const PreloadCheck::Errors& errors) {
207   DCHECK_CURRENTLY_ON(BrowserThread::UI);
208 
209   if (errors.empty()) {
210     InstallExtension();
211     return;
212   }
213 
214   base::string16 error_message;
215   if (errors.count(PreloadCheck::DISALLOWED_BY_POLICY))
216     error_message = policy_check_->GetErrorMessage();
217   else
218     error_message = requirements_check_->GetErrorMessage();
219 
220   DCHECK(!error_message.empty());
221   ReportExtensionLoadError(base::UTF16ToUTF8(error_message));
222 }
223 
GetFlags()224 int UnpackedInstaller::GetFlags() {
225   std::string id = crx_file::id_util::GenerateIdForPath(extension_path_);
226   bool allow_file_access =
227       Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
228   ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
229   if (prefs->HasAllowFileAccessSetting(id))
230     allow_file_access = prefs->AllowFileAccess(id);
231 
232   int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
233   if (allow_file_access)
234     result |= Extension::ALLOW_FILE_ACCESS;
235   if (require_modern_manifest_version_)
236     result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
237 
238   return result;
239 }
240 
LoadExtension(Manifest::Location location,int flags,std::string * error)241 bool UnpackedInstaller::LoadExtension(Manifest::Location location,
242                                       int flags,
243                                       std::string* error) {
244   // Clean up the kMetadataFolder if necessary. This prevents spurious
245   // warnings/errors and ensures we don't treat a user provided file as one by
246   // the Extension system.
247   MaybeCleanupMetadataFolder(extension_path_);
248 
249   // Treat presence of illegal filenames as a hard error for unpacked
250   // extensions. Don't do so for command line extensions since this breaks
251   // Chrome OS autotests (crbug.com/764787).
252   if (location == Manifest::UNPACKED &&
253       !file_util::CheckForIllegalFilenames(extension_path_, error)) {
254     return false;
255   }
256 
257   extension_ =
258       file_util::LoadExtension(extension_path_, location, flags, error);
259 
260   return extension() &&
261          extension_l10n_util::ValidateExtensionLocales(
262              extension_path_, extension()->manifest()->value(), error) &&
263          IndexAndPersistRulesIfNeeded(error);
264 }
265 
IndexAndPersistRulesIfNeeded(std::string * error)266 bool UnpackedInstaller::IndexAndPersistRulesIfNeeded(std::string* error) {
267   DCHECK(extension());
268 
269   // TODO(crbug.com/761107): IndexStaticRulesetsUnsafe will read and parse JSON
270   // synchronously. Change this so that we don't need to parse JSON in the
271   // browser process.
272   declarative_net_request::IndexHelper::Result result =
273       declarative_net_request::IndexHelper::IndexStaticRulesetsUnsafe(
274           *extension());
275   if (result.error) {
276     *error = std::move(*result.error);
277     return false;
278   }
279 
280   ruleset_install_prefs_ = std::move(result.ruleset_install_prefs);
281   if (!result.warnings.empty())
282     extension_->AddInstallWarnings(std::move(result.warnings));
283 
284   return true;
285 }
286 
IsLoadingUnpackedAllowed() const287 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
288   if (!service_weak_.get())
289     return true;
290   // If there is a "*" in the extension blocklist, then no extensions should be
291   // allowed at all (except explicitly allowlisted extensions).
292   return !ExtensionManagementFactory::GetForBrowserContext(
293               service_weak_->profile())
294               ->BlocklistedByDefault();
295 }
296 
GetAbsolutePath()297 void UnpackedInstaller::GetAbsolutePath() {
298   extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
299 
300   // Set priority explicitly to avoid unwanted task priority inheritance.
301   content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
302       ->PostTask(
303           FROM_HERE,
304           base::BindOnce(&UnpackedInstaller::CheckExtensionFileAccess, this));
305 }
306 
CheckExtensionFileAccess()307 void UnpackedInstaller::CheckExtensionFileAccess() {
308   DCHECK_CURRENTLY_ON(BrowserThread::UI);
309   if (!service_weak_.get())
310     return;
311 
312   if (!IsLoadingUnpackedAllowed()) {
313     ReportExtensionLoadError(kUnpackedExtensionsBlocklistedError);
314     return;
315   }
316 
317   GetExtensionFileTaskRunner()->PostTask(
318       FROM_HERE,
319       base::BindOnce(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
320 }
321 
LoadWithFileAccess(int flags)322 void UnpackedInstaller::LoadWithFileAccess(int flags) {
323   std::string error;
324   if (!LoadExtension(Manifest::UNPACKED, flags, &error)) {
325     // Set priority explicitly to avoid unwanted task priority inheritance.
326     content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
327         ->PostTask(FROM_HERE,
328                    base::BindOnce(&UnpackedInstaller::ReportExtensionLoadError,
329                                   this, error));
330     return;
331   }
332 
333   // Set priority explicitly to avoid unwanted task priority inheritance.
334   content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
335       ->PostTask(FROM_HERE,
336                  base::BindOnce(&UnpackedInstaller::StartInstallChecks, this));
337 }
338 
ReportExtensionLoadError(const std::string & error)339 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
340   DCHECK_CURRENTLY_ON(BrowserThread::UI);
341 
342   if (service_weak_.get()) {
343     LoadErrorReporter::GetInstance()->ReportLoadError(
344         extension_path_, error, service_weak_->profile(), be_noisy_on_failure_);
345   }
346 
347   if (!callback_.is_null())
348     std::move(callback_).Run(nullptr, extension_path_, error);
349 }
350 
InstallExtension()351 void UnpackedInstaller::InstallExtension() {
352   DCHECK_CURRENTLY_ON(BrowserThread::UI);
353 
354   if (!service_weak_.get()) {
355     callback_.Reset();
356     return;
357   }
358 
359   PermissionsUpdater perms_updater(service_weak_->profile());
360   perms_updater.InitializePermissions(extension());
361   perms_updater.GrantActivePermissions(extension());
362 
363   service_weak_->OnExtensionInstalled(extension(), syncer::StringOrdinal(),
364                                       kInstallFlagInstallImmediately,
365                                       ruleset_install_prefs_);
366 
367   if (!callback_.is_null())
368     std::move(callback_).Run(extension(), extension_path_, std::string());
369 }
370 
371 }  // namespace extensions
372