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.
5 #include "chrome/browser/component_updater/component_unpacker.h"
10 #include "base/file_util.h"
11 #include "base/json/json_file_value_serializer.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_handle.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/values.h"
17 #include "chrome/browser/component_updater/component_patcher.h"
18 #include "chrome/browser/component_updater/component_updater_service.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "crypto/secure_hash.h"
21 #include "crypto/signature_verifier.h"
22 #include "extensions/common/crx_file.h"
23 #include "third_party/zlib/google/zip.h"
25 using crypto::SecureHash
;
27 namespace component_updater
{
31 // This class makes sure that the CRX digital signature is valid
35 explicit CRXValidator(FILE* crx_file
) : valid_(false), delta_(false) {
36 extensions::CrxFile::Header header
;
37 size_t len
= fread(&header
, 1, sizeof(header
), crx_file
);
38 if (len
< sizeof(header
))
41 extensions::CrxFile::Error error
;
42 scoped_ptr
<extensions::CrxFile
> crx(
43 extensions::CrxFile::Parse(header
, &error
));
46 delta_
= extensions::CrxFile::HeaderIsDelta(header
);
48 std::vector
<uint8
> key(header
.key_size
);
49 len
= fread(&key
[0], sizeof(uint8
), header
.key_size
, crx_file
);
50 if (len
< header
.key_size
)
53 std::vector
<uint8
> signature(header
.signature_size
);
54 len
= fread(&signature
[0], sizeof(uint8
), header
.signature_size
, crx_file
);
55 if (len
< header
.signature_size
)
58 crypto::SignatureVerifier verifier
;
59 if (!verifier
.VerifyInit(extension_misc::kSignatureAlgorithm
,
60 sizeof(extension_misc::kSignatureAlgorithm
),
61 &signature
[0], signature
.size(),
62 &key
[0], key
.size())) {
63 // Signature verification initialization failed. This is most likely
64 // caused by a public key in the wrong format (should encode algorithm).
68 const size_t kBufSize
= 8 * 1024;
69 scoped_ptr
<uint8
[]> buf(new uint8
[kBufSize
]);
70 while ((len
= fread(buf
.get(), 1, kBufSize
, crx_file
)) > 0)
71 verifier
.VerifyUpdate(buf
.get(), len
);
73 if (!verifier
.VerifyFinal())
76 public_key_
.swap(key
);
80 bool valid() const { return valid_
; }
82 bool delta() const { return delta_
; }
84 const std::vector
<uint8
>& public_key() const { return public_key_
; }
89 std::vector
<uint8
> public_key_
;
94 // TODO(cpu): add a specific attribute check to a component json that the
95 // extension unpacker will reject, so that a component cannot be installed
97 scoped_ptr
<base::DictionaryValue
> ReadManifest(
98 const base::FilePath
& unpack_path
) {
99 base::FilePath manifest
=
100 unpack_path
.Append(FILE_PATH_LITERAL("manifest.json"));
101 if (!base::PathExists(manifest
))
102 return scoped_ptr
<base::DictionaryValue
>();
103 JSONFileValueSerializer
serializer(manifest
);
105 scoped_ptr
<base::Value
> root(serializer
.Deserialize(NULL
, &error
));
107 return scoped_ptr
<base::DictionaryValue
>();
108 if (!root
->IsType(base::Value::TYPE_DICTIONARY
))
109 return scoped_ptr
<base::DictionaryValue
>();
110 return scoped_ptr
<base::DictionaryValue
>(
111 static_cast<base::DictionaryValue
*>(root
.release())).Pass();
114 ComponentUnpacker::ComponentUnpacker(const std::vector
<uint8
>& pk_hash
,
115 const base::FilePath
& path
,
116 const std::string
& fingerprint
,
117 ComponentPatcher
* patcher
,
118 ComponentInstaller
* installer
)
121 if (pk_hash
.empty() || path
.empty()) {
122 error_
= kInvalidParams
;
125 // First, validate the CRX header and signature. As of today
126 // this is SHA1 with RSA 1024.
127 ScopedStdioHandle
file(base::OpenFile(path
, "rb"));
129 error_
= kInvalidFile
;
132 CRXValidator
validator(file
.get());
133 if (!validator
.valid()) {
134 error_
= kInvalidFile
;
139 // File is valid and the digital signature matches. Now make sure
140 // the public key hash matches the expected hash. If they do we fully
143 scoped_ptr
<SecureHash
> sha256(SecureHash::Create(SecureHash::SHA256
));
144 sha256
->Update(&(validator
.public_key()[0]), validator
.public_key().size());
145 sha256
->Finish(hash
, arraysize(hash
));
147 if (!std::equal(pk_hash
.begin(), pk_hash
.end(), hash
)) {
151 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
153 error_
= kUnzipPathError
;
156 if (validator
.delta()) { // Package is a diff package.
157 // We want a different temp directory for the delta files; we'll put the
158 // patch output into unpack_path_.
159 base::FilePath unpack_diff_path
;
160 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
161 &unpack_diff_path
)) {
162 error_
= kUnzipPathError
;
165 if (!zip::Unzip(path
, unpack_diff_path
)) {
166 error_
= kUnzipFailed
;
169 ComponentUnpacker::Error result
= DifferentialUpdatePatch(unpack_diff_path
,
174 base::DeleteFile(unpack_diff_path
, true);
175 unpack_diff_path
.clear();
177 if (error_
!= kNone
) {
181 // Package is a normal update/install; unzip it into unpack_path_ directly.
182 if (!zip::Unzip(path
, unpack_path_
)) {
183 error_
= kUnzipFailed
;
187 scoped_ptr
<base::DictionaryValue
> manifest(ReadManifest(unpack_path_
));
188 if (!manifest
.get()) {
189 error_
= kBadManifest
;
192 // Write the fingerprint to disk.
193 if (static_cast<int>(fingerprint
.size()) !=
194 file_util::WriteFile(
195 unpack_path_
.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
197 fingerprint
.size())) {
198 error_
= kFingerprintWriteFailed
;
201 if (!installer
->Install(*manifest
, unpack_path_
)) {
202 error_
= kInstallerError
;
205 // Installation successful. The directory is not our concern now.
206 unpack_path_
.clear();
209 ComponentUnpacker::~ComponentUnpacker() {
210 if (!unpack_path_
.empty())
211 base::DeleteFile(unpack_path_
, true);
214 } // namespace component_updater