1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_SANDBOXED_UNPACKER_H_
#define EXTENSIONS_BROWSER_SANDBOXED_UNPACKER_H_
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "extensions/browser/api/declarative_net_request/install_index_helper.h"
#include "extensions/browser/content_verifier/content_verifier_key.h"
#include "extensions/browser/crx_file_info.h"
#include "extensions/browser/image_sanitizer.h"
#include "extensions/browser/install/crx_install_error.h"
#include "extensions/browser/json_file_sanitizer.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest.h"
#include "extensions/common/mojom/manifest.mojom-shared.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
class SkBitmap;
namespace base {
class SequencedTaskRunner;
}
namespace crx_file {
enum class VerifierFormat;
}
namespace extensions {
class Extension;
enum class SandboxedUnpackerFailureReason;
enum class InstallationStage;
struct RulesetParseResult;
namespace declarative_net_request {
struct IndexAndPersistJSONRulesetResult;
}
class SandboxedUnpackerClient
: public base::RefCountedDeleteOnSequence<SandboxedUnpackerClient> {
public:
// Initialize the ref-counted base to always delete on the UI thread. Note
// the constructor call must also happen on the UI thread.
SandboxedUnpackerClient();
// Determines whether `extension` requires computing and storing
// computed_hashes.json and returns the result through `callback`.
// Currently we do this only for force-installed extensions outside of Chrome
// Web Store, and that is reflected in method's name.
virtual void ShouldComputeHashesForOffWebstoreExtension(
scoped_refptr<const Extension> extension,
base::OnceCallback<void(bool)> callback);
// Since data for content verification (verifier_contents.json) may be present
// in the CRX header, we need to verify it against public key. Normally it is
// Chrome Web Store public key, but may be overridden for tests.
virtual void GetContentVerifierKey(
base::OnceCallback<void(ContentVerifierKey)> callback);
// temp_dir - A temporary directory containing the results of the extension
// unpacking. The client is responsible for deleting this directory.
//
// extension_root - The path to the extension root inside of temp_dir.
//
// original_manifest - The parsed but unmodified version of the manifest,
// with no modifications such as localization, etc.
//
// extension - The extension that was unpacked. The client is responsible
// for deleting this memory.
//
// install_icon - The icon we will display in the installation UI, if any.
//
// ruleset_install_prefs - Install prefs needed for the Declarative Net
// Request API.
//
// Note: OnUnpackSuccess/Failure may be called either synchronously or
// asynchronously from SandboxedUnpacker::StartWithCrx/Directory.
virtual void OnUnpackSuccess(
const base::FilePath& temp_dir,
const base::FilePath& extension_root,
std::unique_ptr<base::Value::Dict> original_manifest,
const Extension* extension,
const SkBitmap& install_icon,
base::Value::Dict ruleset_install_prefs) = 0;
virtual void OnUnpackFailure(const CrxInstallError& error) = 0;
// Called after stage of installation is changed.
virtual void OnStageChanged(InstallationStage stage) {}
protected:
friend class base::RefCountedDeleteOnSequence<SandboxedUnpackerClient>;
friend class base::DeleteHelper<SandboxedUnpackerClient>;
virtual ~SandboxedUnpackerClient() = default;
};
// SandboxedUnpacker does work to optionally unpack and then validate/sanitize
// an extension, either starting from a crx file, or else an already unzipped
// directory (eg., from a differential update). The parsing of complex data
// formats like JPEG or JSON is performed in specific, sandboxed services.
//
// Unpacking an extension using this class makes changes to its source, such as
// transcoding all images to PNG, parsing all message catalogs, and rewriting
// the manifest JSON. As such, it should not be used when the output is not
// intended to be given back to the author.
class SandboxedUnpacker : public ImageSanitizer::Client {
public:
// Overrides the required verifier format for testing purposes. Only one
// ScopedVerifierFormatOverrideForTest may exist at a time.
class ScopedVerifierFormatOverrideForTest {
public:
explicit ScopedVerifierFormatOverrideForTest(
crx_file::VerifierFormat format);
~ScopedVerifierFormatOverrideForTest();
private:
THREAD_CHECKER(thread_checker_);
};
// Creates a SandboxedUnpacker that will do work to unpack an extension,
// passing the `location` and `creation_flags` to Extension::Create. The
// `extensions_dir` parameter should specify the directory under which we'll
// create a subdirectory to write the unpacked extension contents.
// Note: Because this requires disk I/O, the task runner passed should use
// TaskShutdownBehavior::SKIP_ON_SHUTDOWN to ensure that either the task is
// fully run (if initiated before shutdown) or not run at all (if shutdown is
// initiated first). See crbug.com/235525.
// TODO(devlin): SKIP_ON_SHUTDOWN is also not quite sufficient for this. We
// should probably instead be using base::ImportantFileWriter or similar.
SandboxedUnpacker(
mojom::ManifestLocation location,
int creation_flags,
const base::FilePath& extensions_dir,
const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
SandboxedUnpackerClient* client);
SandboxedUnpacker(const SandboxedUnpacker&) = delete;
SandboxedUnpacker& operator=(const SandboxedUnpacker&) = delete;
// Start processing the extension, either from a CRX file or already unzipped
// in a directory. The client is called with the results. The directory form
// requires the id and base64-encoded public key (for insertion into the
// 'key' field of the manifest.json file).
void StartWithCrx(const CRXFileInfo& crx_info);
void StartWithDirectory(const ExtensionId& extension_id,
const std::string& public_key_base64,
const base::FilePath& directory);
private:
friend class SandboxedUnpackerTest;
class IOThreadState;
~SandboxedUnpacker() override;
// Create `temp_dir_` used to unzip or unpack the extension in.
bool CreateTempDirectory();
// Helper functions to simplify calling ReportFailure.
std::u16string FailureReasonToString16(
const SandboxedUnpackerFailureReason reason);
void FailWithPackageError(const SandboxedUnpackerFailureReason reason);
// Validates the signature of the extension and extract the key to
// `public_key_`. True if the signature validates, false otherwise.
bool ValidateSignature(const base::FilePath& crx_path,
const std::string& expected_hash,
const crx_file::VerifierFormat required_format);
// Unzips the extension into directory.
void Unzip(const base::FilePath& crx_path,
const base::FilePath& unzipped_dir);
void UnzipDone(const base::FilePath& zip_file,
const base::FilePath& unzip_dir,
const std::string& error);
// Callback which is called after the verified contents are uncompressed.
void OnVerifiedContentsUncompressed(
const base::FilePath& unzip_dir,
base::expected<mojo_base::BigBuffer, std::string> result);
// Verifies the decompressed verified contents fetched from the header of CRX
// and stores them if the verification of these contents is successful.
void StoreVerifiedContentsInExtensionDir(
const base::FilePath& unzip_dir,
base::span<const uint8_t> verified_contents,
ContentVerifierKey content_verifier_key);
// Unpacks the extension in directory and returns the manifest.
void Unpack(const base::FilePath& directory);
void ReadManifestDone(base::expected<base::Value, std::string> result);
void UnpackExtensionSucceeded(base::Value::Dict manifest);
// Helper which calls ReportFailure.
void ReportUnpackExtensionFailed(std::string_view error);
// Implementation of ImageSanitizer::Client:
data_decoder::DataDecoder* GetDataDecoder() override;
void OnImageSanitizationDone(ImageSanitizer::Status status,
const base::FilePath& path) override;
void OnImageDecoded(const base::FilePath& path, SkBitmap image) override;
void ReadMessageCatalogs();
void SanitizeMessageCatalogs(
const std::set<base::FilePath>& message_catalog_paths);
void MessageCatalogsSanitized(
base::expected<void, JsonFileSanitizer::Error> result);
// Reports unpack success or failure, or unzip failure.
void ReportSuccess();
// Puts a sanboxed unpacker failure in histogram
// Extensions.SandboxUnpackFailureReason.
void ReportFailure(const SandboxedUnpackerFailureReason reason,
const std::u16string& error);
// Overwrites original manifest with safe result from utility process.
// Returns nullopt on error.
std::optional<base::Value::Dict> RewriteManifestFile(
const base::Value::Dict& manifest);
// Cleans up temp directory artifacts.
void Cleanup();
// If a Declarative Net Request JSON ruleset is present, parses the JSON
// rulesets for the Declarative Net Request API and persists the indexed
// rulesets.
void IndexAndPersistJSONRulesetsIfNeeded();
void OnJSONRulesetsIndexed(RulesetParseResult result);
// Computed hashes: if requested (via ShouldComputeHashes callback in
// SandbloxedUnpackerClient), calculate hashes of all extensions' resources
// and writes them in _metadata/computed_hashes.json. This is used by content
// verification system for extensions outside of Chrome Web Store.
void CheckComputeHashes();
void MaybeComputeHashes(bool should_compute_hashes);
// Parses the JSON file at `path` and invokes `ReadManifestDone()` with the
// result.
// This must be called from the `unpacker_io_task_runner_`.
void ParseJsonFile(const base::FilePath& path);
// If we unpacked a CRX file, we hold on to the path name for use
// in various histograms.
base::FilePath crx_path_for_histograms_;
// Our unpacker client.
scoped_refptr<SandboxedUnpackerClient> client_;
// The Extensions directory inside the profile.
base::FilePath extensions_dir_;
// Temporary directory to use for unpacking.
base::ScopedTempDir temp_dir_;
// Root directory of the unpacked extension (a child of temp_dir_).
base::FilePath extension_root_;
// Parsed original manifest of the extension. Set after unpacking the
// extension and working with its manifest, so after UnpackExtensionSucceeded
// is called.
std::optional<base::Value::Dict> manifest_;
// Install prefs needed for the Declarative Net Request API.
base::Value::Dict ruleset_install_prefs_;
// Represents the extension we're unpacking.
scoped_refptr<Extension> extension_;
// The compressed verified contents extracted from the CRX header.
std::vector<uint8_t> compressed_verified_contents_;
// The public key that was extracted from the CRX header.
std::string public_key_;
// The extension's ID. This will be calculated from the public key
// in the CRX header.
ExtensionId extension_id_;
// Location to use for the unpacked extension.
mojom::ManifestLocation location_;
// Creation flags to use for the extension. These flags will be used
// when calling Extension::Create() by the CRX installer.
int creation_flags_;
// Overridden value of VerifierFormat that is used from StartWithCrx().
std::optional<crx_file::VerifierFormat> format_verifier_override_;
// Sequenced task runner where file I/O operations will be performed.
scoped_refptr<base::SequencedTaskRunner> unpacker_io_task_runner_;
// The normalized path of the install icon path, retrieved from the manifest.
base::FilePath install_icon_path_;
// The decoded install icon.
SkBitmap install_icon_;
// TODO(crbug.com/40232388): Consider to wrap it in base::SequenceBound
std::unique_ptr<IOThreadState> io_thread_state_;
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_SANDBOXED_UNPACKER_H_
|