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.
4
5#include "chrome/browser/component_updater/component_unpacker.h"
6
7#include <string>
8#include <vector>
9
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"
24
25using crypto::SecureHash;
26
27namespace {
28
29// This class makes sure that the CRX digital signature is valid
30// and well formed.
31class CRXValidator {
32 public:
33  explicit CRXValidator(FILE* crx_file) : valid_(false), delta_(false) {
34    extensions::CrxFile::Header header;
35    size_t len = fread(&header, 1, sizeof(header), crx_file);
36    if (len < sizeof(header))
37      return;
38
39    extensions::CrxFile::Error error;
40    scoped_ptr<extensions::CrxFile> crx(
41        extensions::CrxFile::Parse(header, &error));
42    if (!crx.get())
43      return;
44    delta_ = extensions::CrxFile::HeaderIsDelta(header);
45
46    std::vector<uint8> key(header.key_size);
47    len = fread(&key[0], sizeof(uint8), header.key_size, crx_file);
48    if (len < header.key_size)
49      return;
50
51    std::vector<uint8> signature(header.signature_size);
52    len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file);
53    if (len < header.signature_size)
54      return;
55
56    crypto::SignatureVerifier verifier;
57    if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm,
58                             sizeof(extension_misc::kSignatureAlgorithm),
59                             &signature[0], signature.size(),
60                             &key[0], key.size())) {
61      // Signature verification initialization failed. This is most likely
62      // caused by a public key in the wrong format (should encode algorithm).
63      return;
64    }
65
66    const size_t kBufSize = 8 * 1024;
67    scoped_ptr<uint8[]> buf(new uint8[kBufSize]);
68    while ((len = fread(buf.get(), 1, kBufSize, crx_file)) > 0)
69      verifier.VerifyUpdate(buf.get(), len);
70
71    if (!verifier.VerifyFinal())
72      return;
73
74    public_key_.swap(key);
75    valid_ = true;
76  }
77
78  bool valid() const { return valid_; }
79
80  bool delta() const { return delta_; }
81
82  const std::vector<uint8>& public_key() const { return public_key_; }
83
84 private:
85  bool valid_;
86  bool delta_;
87  std::vector<uint8> public_key_;
88};
89
90}  // namespace.
91
92// TODO(cpu): add a specific attribute check to a component json that the
93// extension unpacker will reject, so that a component cannot be installed
94// as an extension.
95scoped_ptr<base::DictionaryValue> ReadManifest(
96    const base::FilePath& unpack_path) {
97  base::FilePath manifest =
98      unpack_path.Append(FILE_PATH_LITERAL("manifest.json"));
99  if (!base::PathExists(manifest))
100    return scoped_ptr<base::DictionaryValue>();
101  JSONFileValueSerializer serializer(manifest);
102  std::string error;
103  scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
104  if (!root.get())
105    return scoped_ptr<base::DictionaryValue>();
106  if (!root->IsType(base::Value::TYPE_DICTIONARY))
107    return scoped_ptr<base::DictionaryValue>();
108  return scoped_ptr<base::DictionaryValue>(
109      static_cast<base::DictionaryValue*>(root.release())).Pass();
110}
111
112ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
113                                     const base::FilePath& path,
114                                     const std::string& fingerprint,
115                                     ComponentPatcher* patcher,
116                                     ComponentInstaller* installer)
117    : error_(kNone),
118      extended_error_(0) {
119  if (pk_hash.empty() || path.empty()) {
120    error_ = kInvalidParams;
121    return;
122  }
123  // First, validate the CRX header and signature. As of today
124  // this is SHA1 with RSA 1024.
125  ScopedStdioHandle file(base::OpenFile(path, "rb"));
126  if (!file.get()) {
127    error_ = kInvalidFile;
128    return;
129  }
130  CRXValidator validator(file.get());
131  if (!validator.valid()) {
132    error_ = kInvalidFile;
133    return;
134  }
135  file.Close();
136
137  // File is valid and the digital signature matches. Now make sure
138  // the public key hash matches the expected hash. If they do we fully
139  // trust this CRX.
140  uint8 hash[32];
141  scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
142  sha256->Update(&(validator.public_key()[0]), validator.public_key().size());
143  sha256->Finish(hash, arraysize(hash));
144
145  if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) {
146    error_ = kInvalidId;
147    return;
148  }
149  if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
150                                    &unpack_path_)) {
151    error_ = kUnzipPathError;
152    return;
153  }
154  if (validator.delta()) {  // Package is a diff package.
155    // We want a different temp directory for the delta files; we'll put the
156    // patch output into unpack_path_.
157    base::FilePath unpack_diff_path;
158    if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
159                                      &unpack_diff_path)) {
160      error_ = kUnzipPathError;
161      return;
162    }
163    if (!zip::Unzip(path, unpack_diff_path)) {
164      error_ = kUnzipFailed;
165      return;
166    }
167    ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path,
168                                                              unpack_path_,
169                                                              patcher,
170                                                              installer,
171                                                              &extended_error_);
172    base::DeleteFile(unpack_diff_path, true);
173    unpack_diff_path.clear();
174    error_ = result;
175    if (error_ != kNone) {
176      return;
177    }
178  } else {
179    // Package is a normal update/install; unzip it into unpack_path_ directly.
180    if (!zip::Unzip(path, unpack_path_)) {
181      error_ = kUnzipFailed;
182      return;
183    }
184  }
185  scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_));
186  if (!manifest.get()) {
187    error_ = kBadManifest;
188    return;
189  }
190  // Write the fingerprint to disk.
191  if (static_cast<int>(fingerprint.size()) !=
192      file_util::WriteFile(
193          unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
194          fingerprint.c_str(),
195          fingerprint.size())) {
196    error_ = kFingerprintWriteFailed;
197    return;
198  }
199  if (!installer->Install(*manifest, unpack_path_)) {
200    error_ = kInstallerError;
201    return;
202  }
203  // Installation successful. The directory is not our concern now.
204  unpack_path_.clear();
205}
206
207ComponentUnpacker::~ComponentUnpacker() {
208  if (!unpack_path_.empty())
209    base::DeleteFile(unpack_path_, true);
210}
211