File: sandboxed_unpacker.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (321 lines) | stat: -rw-r--r-- 12,738 bytes parent folder | download | duplicates (5)
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_