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 "base/bind.h" 8 #include "base/callback.h" 9 #include "base/files/file_util.h" 10 #include "base/strings/string_util.h" 11 #include "base/threading/thread_restrictions.h" 12 #include "chrome/browser/extensions/extension_error_reporter.h" 13 #include "chrome/browser/extensions/extension_install_prompt.h" 14 #include "chrome/browser/extensions/extension_install_ui.h" 15 #include "chrome/browser/extensions/extension_management.h" 16 #include "chrome/browser/extensions/extension_service.h" 17 #include "chrome/browser/extensions/permissions_updater.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/common/extensions/api/plugins/plugins_handler.h" 20 #include "components/crx_file/id_util.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "extensions/browser/extension_prefs.h" 23 #include "extensions/browser/extension_registry.h" 24 #include "extensions/browser/install_flag.h" 25 #include "extensions/common/extension.h" 26 #include "extensions/common/extension_l10n_util.h" 27 #include "extensions/common/file_util.h" 28 #include "extensions/common/manifest.h" 29 #include "sync/api/string_ordinal.h" 30 31 using content::BrowserThread; 32 using extensions::Extension; 33 34 namespace { 35 36 const char kUnpackedExtensionsBlacklistedError[] = 37 "Loading of unpacked extensions is disabled by the administrator."; 38 39 // Manages an ExtensionInstallPrompt for a particular extension. 40 class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate { 41 public: 42 SimpleExtensionLoadPrompt(const Extension* extension, 43 Profile* profile, 44 const base::Closure& callback); 45 virtual ~SimpleExtensionLoadPrompt(); 46 47 void ShowPrompt(); 48 49 // ExtensionInstallUI::Delegate 50 virtual void InstallUIProceed() OVERRIDE; 51 virtual void InstallUIAbort(bool user_initiated) OVERRIDE; 52 53 private: 54 scoped_ptr<ExtensionInstallPrompt> install_ui_; 55 scoped_refptr<const Extension> extension_; 56 base::Closure callback_; 57 }; 58 59 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt( 60 const Extension* extension, 61 Profile* profile, 62 const base::Closure& callback) 63 : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile( 64 profile)), 65 extension_(extension), 66 callback_(callback) { 67 } 68 69 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() { 70 } 71 72 void SimpleExtensionLoadPrompt::ShowPrompt() { 73 switch (ExtensionInstallPrompt::g_auto_confirm_for_tests) { 74 case ExtensionInstallPrompt::NONE: 75 install_ui_->ConfirmInstall( 76 this, 77 extension_.get(), 78 ExtensionInstallPrompt::GetDefaultShowDialogCallback()); 79 break; 80 case ExtensionInstallPrompt::ACCEPT: 81 InstallUIProceed(); 82 break; 83 case ExtensionInstallPrompt::CANCEL: 84 InstallUIAbort(false); 85 } 86 } 87 88 void SimpleExtensionLoadPrompt::InstallUIProceed() { 89 callback_.Run(); 90 delete this; 91 } 92 93 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) { 94 delete this; 95 } 96 97 } // namespace 98 99 namespace extensions { 100 101 // static 102 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create( 103 ExtensionService* extension_service) { 104 DCHECK(extension_service); 105 return scoped_refptr<UnpackedInstaller>( 106 new UnpackedInstaller(extension_service)); 107 } 108 109 UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service) 110 : service_weak_(extension_service->AsWeakPtr()), 111 prompt_for_plugins_(true), 112 require_modern_manifest_version_(true), 113 be_noisy_on_failure_(true), 114 install_checker_(extension_service->profile()) { 115 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 116 } 117 118 UnpackedInstaller::~UnpackedInstaller() { 119 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || 120 BrowserThread::CurrentlyOn(BrowserThread::FILE)); 121 } 122 123 void UnpackedInstaller::Load(const base::FilePath& path_in) { 124 DCHECK(extension_path_.empty()); 125 extension_path_ = path_in; 126 BrowserThread::PostTask( 127 BrowserThread::FILE, 128 FROM_HERE, 129 base::Bind(&UnpackedInstaller::GetAbsolutePath, this)); 130 } 131 132 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in, 133 std::string* extension_id) { 134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 135 DCHECK(extension_path_.empty()); 136 137 if (!service_weak_.get()) 138 return false; 139 // Load extensions from the command line synchronously to avoid a race 140 // between extension loading and loading an URL from the command line. 141 base::ThreadRestrictions::ScopedAllowIO allow_io; 142 143 extension_path_ = base::MakeAbsoluteFilePath(path_in); 144 145 if (!IsLoadingUnpackedAllowed()) { 146 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError); 147 return false; 148 } 149 150 std::string error; 151 install_checker_.set_extension( 152 file_util::LoadExtension( 153 extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get()); 154 155 if (!extension() || 156 !extension_l10n_util::ValidateExtensionLocales( 157 extension_path_, extension()->manifest()->value(), &error)) { 158 ReportExtensionLoadError(error); 159 return false; 160 } 161 162 PermissionsUpdater( 163 service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT) 164 .InitializePermissions(extension()); 165 ShowInstallPrompt(); 166 167 *extension_id = extension()->id(); 168 return true; 169 } 170 171 void UnpackedInstaller::ShowInstallPrompt() { 172 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 173 if (!service_weak_.get()) 174 return; 175 176 const ExtensionSet& disabled_extensions = 177 ExtensionRegistry::Get(service_weak_->profile())->disabled_extensions(); 178 if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ && 179 PluginInfo::HasPlugins(extension()) && 180 !disabled_extensions.Contains(extension()->id())) { 181 SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt( 182 extension(), 183 install_checker_.profile(), 184 base::Bind(&UnpackedInstaller::StartInstallChecks, this)); 185 prompt->ShowPrompt(); 186 return; 187 } 188 StartInstallChecks(); 189 } 190 191 void UnpackedInstaller::StartInstallChecks() { 192 install_checker_.Start( 193 ExtensionInstallChecker::CHECK_REQUIREMENTS | 194 ExtensionInstallChecker::CHECK_MANAGEMENT_POLICY, 195 true /* fail fast */, 196 base::Bind(&UnpackedInstaller::OnInstallChecksComplete, this)); 197 } 198 199 void UnpackedInstaller::OnInstallChecksComplete(int failed_checks) { 200 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 201 202 if (!install_checker_.policy_error().empty()) { 203 ReportExtensionLoadError(install_checker_.policy_error()); 204 return; 205 } 206 207 if (!install_checker_.requirement_errors().empty()) { 208 ReportExtensionLoadError( 209 JoinString(install_checker_.requirement_errors(), ' ')); 210 return; 211 } 212 213 InstallExtension(); 214 } 215 216 int UnpackedInstaller::GetFlags() { 217 std::string id = crx_file::id_util::GenerateIdForPath(extension_path_); 218 bool allow_file_access = 219 Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED); 220 ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile()); 221 if (prefs->HasAllowFileAccessSetting(id)) 222 allow_file_access = prefs->AllowFileAccess(id); 223 224 int result = Extension::FOLLOW_SYMLINKS_ANYWHERE; 225 if (allow_file_access) 226 result |= Extension::ALLOW_FILE_ACCESS; 227 if (require_modern_manifest_version_) 228 result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION; 229 230 return result; 231 } 232 233 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const { 234 if (!service_weak_.get()) 235 return true; 236 // If there is a "*" in the extension blacklist, then no extensions should be 237 // allowed at all (except explicitly whitelisted extensions). 238 return !ExtensionManagementFactory::GetForBrowserContext( 239 service_weak_->profile())->BlacklistedByDefault(); 240 } 241 242 void UnpackedInstaller::GetAbsolutePath() { 243 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 244 245 extension_path_ = base::MakeAbsoluteFilePath(extension_path_); 246 247 std::string error; 248 if (!file_util::CheckForIllegalFilenames(extension_path_, &error)) { 249 BrowserThread::PostTask( 250 BrowserThread::UI, 251 FROM_HERE, 252 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error)); 253 return; 254 } 255 BrowserThread::PostTask( 256 BrowserThread::UI, FROM_HERE, 257 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this)); 258 } 259 260 void UnpackedInstaller::CheckExtensionFileAccess() { 261 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 262 if (!service_weak_.get()) 263 return; 264 265 if (!IsLoadingUnpackedAllowed()) { 266 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError); 267 return; 268 } 269 270 BrowserThread::PostTask( 271 BrowserThread::FILE, 272 FROM_HERE, 273 base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags())); 274 } 275 276 void UnpackedInstaller::LoadWithFileAccess(int flags) { 277 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 278 279 std::string error; 280 install_checker_.set_extension( 281 file_util::LoadExtension( 282 extension_path_, Manifest::UNPACKED, flags, &error).get()); 283 284 if (!extension() || 285 !extension_l10n_util::ValidateExtensionLocales( 286 extension_path_, extension()->manifest()->value(), &error)) { 287 BrowserThread::PostTask( 288 BrowserThread::UI, 289 FROM_HERE, 290 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error)); 291 return; 292 } 293 294 BrowserThread::PostTask( 295 BrowserThread::UI, 296 FROM_HERE, 297 base::Bind(&UnpackedInstaller::ShowInstallPrompt, this)); 298 } 299 300 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) { 301 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 302 303 if (service_weak_.get()) { 304 ExtensionErrorReporter::GetInstance()->ReportLoadError( 305 extension_path_, 306 error, 307 service_weak_->profile(), 308 be_noisy_on_failure_); 309 } 310 } 311 312 void UnpackedInstaller::InstallExtension() { 313 DCHECK_CURRENTLY_ON(BrowserThread::UI); 314 315 PermissionsUpdater perms_updater(service_weak_->profile()); 316 perms_updater.InitializePermissions(extension()); 317 perms_updater.GrantActivePermissions(extension()); 318 319 service_weak_->OnExtensionInstalled( 320 extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately); 321 } 322 323 } // namespace extensions 324