Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(214)

Side by Side Diff: chrome/common/extensions/extension_unpacker.cc

Issue 115682: Verify signed .crx extension installations (Closed)
Patch Set: moved extensions consts back to extensions_service (was causing compile errors on mac & linux) Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/common/extensions/extension_unpacker.h" 5 #include "chrome/common/extensions/extension_unpacker.h"
6 6
7 #include "base/file_util.h" 7 #include "base/file_util.h"
8 #include "base/scoped_handle.h" 8 #include "base/scoped_handle.h"
9 #include "base/scoped_temp_dir.h" 9 #include "base/scoped_temp_dir.h"
10 #include "base/string_util.h" 10 #include "base/string_util.h"
11 #include "base/third_party/nss/blapi.h"
12 #include "base/third_party/nss/sha256.h"
13 #include "base/thread.h" 11 #include "base/thread.h"
14 #include "base/values.h" 12 #include "base/values.h"
15 #include "net/base/file_stream.h" 13 #include "net/base/file_stream.h"
16 #include "chrome/common/extensions/extension.h" 14 #include "chrome/common/extensions/extension.h"
17 #include "chrome/common/ipc_message_utils.h" 15 #include "chrome/common/ipc_message_utils.h"
18 #include "chrome/common/json_value_serializer.h" 16 #include "chrome/common/json_value_serializer.h"
19 #include "chrome/common/notification_service.h" 17 #include "chrome/common/notification_service.h"
20 #include "chrome/common/url_constants.h" 18 #include "chrome/common/url_constants.h"
21 #include "chrome/common/zip.h" 19 #include "chrome/common/zip.h"
22 #include "third_party/skia/include/core/SkBitmap.h" 20 #include "third_party/skia/include/core/SkBitmap.h"
23 #include "webkit/glue/image_decoder.h" 21 #include "webkit/glue/image_decoder.h"
24 22
25 namespace { 23 namespace {
26 const char kCurrentVersionFileName[] = "Current Version";
27
28 // The name of a temporary directory to install an extension into for 24 // The name of a temporary directory to install an extension into for
29 // validation before finalizing install. 25 // validation before finalizing install.
30 const char kTempExtensionName[] = "TEMP_INSTALL"; 26 const char kTempExtensionName[] = "TEMP_INSTALL";
31 27
32 // The file to write our decoded images to, relative to the extension_path. 28 // The file to write our decoded images to, relative to the extension_path.
33 const char kDecodedImagesFilename[] = "DECODED_IMAGES"; 29 const char kDecodedImagesFilename[] = "DECODED_IMAGES";
34 30
35 // Chromium Extension magic number 31 // Errors
36 // TODO(aa): This should use the one in ExtensionCreator once we transition this 32 const char* kCouldNotCreateDirectoryError =
37 // to ouptut the same format. 33 "Could not create directory for unzipping.";
38 const char kExtensionFileMagic[] = "Cr24"; 34 const char* kCouldNotDecodeImageError = "Could not decode theme image.";
35 const char* kCouldNotUnzipExtension = "Could not unzip extension.";
36 const char* kPathNamesMustBeAbsoluteOrLocalError =
37 "Path names must not be absolute or contain '..'.";
39 38
40 struct ExtensionHeader {
41 char magic[sizeof(kExtensionFileMagic) - 1];
42 uint32 version;
43 size_t header_size;
44 size_t manifest_size;
45 };
46
47 const size_t kZipHashBytes = 32; // SHA-256
48 const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
49
50 // A marker file to indicate that an extension was installed from an external
51 // source.
52 const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
53
54 // The version of the extension package that this code understands.
55 const uint32 kExpectedVersion = 1;
56 } // namespace 39 } // namespace
57 40
58 static SkBitmap DecodeImage(const FilePath& path) { 41 static SkBitmap DecodeImage(const FilePath& path) {
59 // Read the file from disk. 42 // Read the file from disk.
60 std::string file_contents; 43 std::string file_contents;
61 if (!file_util::PathExists(path) || 44 if (!file_util::PathExists(path) ||
62 !file_util::ReadFileToString(path, &file_contents)) { 45 !file_util::ReadFileToString(path, &file_contents)) {
63 return SkBitmap(); 46 return SkBitmap();
64 } 47 }
65 48
(...skipping 17 matching lines...) Expand all
83 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) { 66 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
84 return true; 67 return true;
85 } 68 }
86 ++i; 69 ++i;
87 } 70 }
88 } 71 }
89 72
90 return false; 73 return false;
91 } 74 }
92 75
93 // The extension file format is a header, followed by the manifest, followed
94 // by the zip file. The header is a magic number, a version, the size of the
95 // header, and the size of the manifest. These ints are 4 byte little endian.
96 DictionaryValue* ExtensionUnpacker::ReadPackageHeader() {
97 ScopedStdioHandle file(file_util::OpenFile(extension_path_, "rb"));
98 if (!file.get()) {
99 SetError("no such extension file");
100 return NULL;
101 }
102
103 // Read and verify the header.
104 ExtensionHeader header;
105 size_t len;
106
107 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
108 // appears that we don't have any endian/alignment aware serialization
109 // code in the code base. So for now, this assumes that we're running
110 // on a little endian machine with 4 byte alignment.
111 len = fread(&header, 1, sizeof(ExtensionHeader), file.get());
112 if (len < sizeof(ExtensionHeader)) {
113 SetError("invalid extension header");
114 return NULL;
115 }
116 if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) {
117 SetError("bad magic number");
118 return NULL;
119 }
120 if (header.version != kExpectedVersion) {
121 SetError("bad version number");
122 return NULL;
123 }
124 if (header.header_size > sizeof(ExtensionHeader))
125 fseek(file.get(), header.header_size - sizeof(ExtensionHeader), SEEK_CUR);
126
127 char buf[1 << 16];
128 std::string manifest_str;
129 size_t read_size = std::min(sizeof(buf), header.manifest_size);
130 size_t remainder = header.manifest_size;
131 while ((len = fread(buf, 1, read_size, file.get())) > 0) {
132 manifest_str.append(buf, len);
133 if (len <= remainder)
134 break;
135 remainder -= len;
136 read_size = std::min(sizeof(buf), remainder);
137 }
138
139 // Verify the JSON
140 JSONStringValueSerializer json(manifest_str);
141 std::string error;
142 scoped_ptr<Value> val(json.Deserialize(&error));
143 if (!val.get()) {
144 SetError(error);
145 return NULL;
146 }
147 if (!val->IsType(Value::TYPE_DICTIONARY)) {
148 SetError("manifest isn't a JSON dictionary");
149 return NULL;
150 }
151 DictionaryValue* manifest = static_cast<DictionaryValue*>(val.get());
152
153 // TODO(erikkay): The manifest will also contain a signature of the hash
154 // (or perhaps the whole manifest) for authentication purposes.
155
156 // The caller owns val (now cast to manifest).
157 val.release();
158 return manifest;
159 }
160
161 DictionaryValue* ExtensionUnpacker::ReadManifest() { 76 DictionaryValue* ExtensionUnpacker::ReadManifest() {
162 FilePath manifest_path = 77 FilePath manifest_path =
163 temp_install_dir_.AppendASCII(Extension::kManifestFilename); 78 temp_install_dir_.AppendASCII(Extension::kManifestFilename);
164 if (!file_util::PathExists(manifest_path)) { 79 if (!file_util::PathExists(manifest_path)) {
165 SetError(Extension::kInvalidManifestError); 80 SetError(Extension::kInvalidManifestError);
166 return NULL; 81 return NULL;
167 } 82 }
168 83
169 JSONFileValueSerializer serializer(manifest_path); 84 JSONFileValueSerializer serializer(manifest_path);
170 std::string error; 85 std::string error;
171 scoped_ptr<Value> root(serializer.Deserialize(&error)); 86 scoped_ptr<Value> root(serializer.Deserialize(&error));
172 if (!root.get()) { 87 if (!root.get()) {
173 SetError(error); 88 SetError(error);
174 return NULL; 89 return NULL;
175 } 90 }
176 91
177 if (!root->IsType(Value::TYPE_DICTIONARY)) { 92 if (!root->IsType(Value::TYPE_DICTIONARY)) {
178 SetError(Extension::kInvalidManifestError); 93 SetError(Extension::kInvalidManifestError);
179 return NULL; 94 return NULL;
180 } 95 }
181 96
182 return static_cast<DictionaryValue*>(root.release()); 97 return static_cast<DictionaryValue*>(root.release());
183 } 98 }
184 99
185 bool ExtensionUnpacker::Run() { 100 bool ExtensionUnpacker::Run() {
186 LOG(INFO) << "Installing extension " << extension_path_.value(); 101 LOG(INFO) << "Installing extension " << extension_path_.value();
187 102
188 // Read and verify the extension.
189 scoped_ptr<DictionaryValue> header_manifest(ReadPackageHeader());
190 if (!header_manifest.get()) {
191 // ReadPackageHeader has already reported the extension error.
192 return false;
193 }
194
195 // TODO(mpcomplete): it looks like this isn't actually necessary. We don't
196 // use header_extension, and we check that the unzipped manifest is valid.
197 Extension header_extension;
198 std::string error;
199 if (!header_extension.InitFromValue(*header_manifest,
200 true, // require ID
201 &error)) {
202 SetError(error);
203 return false;
204 }
205
206 // <profile>/Extensions/INSTALL_TEMP/<version> 103 // <profile>/Extensions/INSTALL_TEMP/<version>
207 temp_install_dir_ = 104 temp_install_dir_ =
208 extension_path_.DirName().AppendASCII(kTempExtensionName); 105 extension_path_.DirName().AppendASCII(kTempExtensionName);
209 if (!file_util::CreateDirectory(temp_install_dir_)) { 106 if (!file_util::CreateDirectory(temp_install_dir_)) {
210 SetError("Couldn't create directory for unzipping."); 107 SetError(kCouldNotCreateDirectoryError);
211 return false; 108 return false;
212 } 109 }
213 110
214 if (!Unzip(extension_path_, temp_install_dir_)) { 111 if (!Unzip(extension_path_, temp_install_dir_)) {
215 SetError("Couldn't unzip extension."); 112 SetError(kCouldNotUnzipExtension);
216 return false; 113 return false;
217 } 114 }
218 115
219 // Parse the manifest. 116 // Parse the manifest.
220 parsed_manifest_.reset(ReadManifest()); 117 parsed_manifest_.reset(ReadManifest());
221 if (!parsed_manifest_.get()) 118 if (!parsed_manifest_.get())
222 return false; // Error was already reported. 119 return false; // Error was already reported.
223 120
224 // Re-read the actual manifest into our extension struct. 121 // NOTE: Since the Unpacker doesn't have the extension's public_id, the
122 // InitFromValue is allowed to generate a temporary id for the extension.
123 // ANY CODE THAT FOLLOWS SHOULD NOT DEPEND ON THE CORRECT ID OF THIS
124 // EXTENSION.
225 Extension extension; 125 Extension extension;
126 std::string error;
226 if (!extension.InitFromValue(*parsed_manifest_, 127 if (!extension.InitFromValue(*parsed_manifest_,
227 true, // require ID 128 false,
228 &error)) { 129 &error)) {
229 SetError(error); 130 SetError(error);
230 return false; 131 return false;
231 } 132 }
232
233 // Decode any images that the browser needs to display. 133 // Decode any images that the browser needs to display.
234 std::set<FilePath> image_paths = extension.GetBrowserImages(); 134 std::set<FilePath> image_paths = extension.GetBrowserImages();
235 for (std::set<FilePath>::iterator it = image_paths.begin(); 135 for (std::set<FilePath>::iterator it = image_paths.begin();
236 it != image_paths.end(); ++it) { 136 it != image_paths.end(); ++it) {
237 if (!AddDecodedImage(*it)) 137 if (!AddDecodedImage(*it))
238 return false; // Error was already reported. 138 return false; // Error was already reported.
239 } 139 }
240 140
241 return true; 141 return true;
242 } 142 }
(...skipping 21 matching lines...) Expand all
264 return false; 164 return false;
265 165
266 IPC::Message pickle(file_str.data(), file_str.size()); 166 IPC::Message pickle(file_str.data(), file_str.size());
267 void* iter = NULL; 167 void* iter = NULL;
268 return IPC::ReadParam(&pickle, &iter, images); 168 return IPC::ReadParam(&pickle, &iter, images);
269 } 169 }
270 170
271 bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) { 171 bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
272 // Make sure it's not referencing a file outside the extension's subdir. 172 // Make sure it's not referencing a file outside the extension's subdir.
273 if (path.IsAbsolute() || PathContainsParentDirectory(path)) { 173 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
274 SetError("Path names must not be absolute or contain '..'."); 174 SetError(kPathNamesMustBeAbsoluteOrLocalError);
275 return false; 175 return false;
276 } 176 }
277 177
278 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path)); 178 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
279 if (image_bitmap.isNull()) { 179 if (image_bitmap.isNull()) {
280 SetError("Could not decode theme image."); 180 SetError(kCouldNotDecodeImageError);
281 return false; 181 return false;
282 } 182 }
283 183
284 decoded_images_.push_back(MakeTuple(image_bitmap, path)); 184 decoded_images_.push_back(MakeTuple(image_bitmap, path));
285 return true; 185 return true;
286 } 186 }
287 187
288 void ExtensionUnpacker::SetError(const std::string &error) { 188 void ExtensionUnpacker::SetError(const std::string &error) {
289 error_message_ = error; 189 error_message_ = error;
290 } 190 }
OLDNEW
« no previous file with comments | « chrome/common/extensions/extension_unpacker.h ('k') | chrome/test/data/extensions/bad/missing_content_script/1/manifest.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698