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

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

Issue 125004: Revert "BUG=12114" (Closed)
Patch Set: 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"
11 #include "base/thread.h" 13 #include "base/thread.h"
12 #include "base/values.h" 14 #include "base/values.h"
13 #include "net/base/file_stream.h" 15 #include "net/base/file_stream.h"
14 #include "chrome/common/extensions/extension.h" 16 #include "chrome/common/extensions/extension.h"
15 #include "chrome/common/ipc_message_utils.h" 17 #include "chrome/common/ipc_message_utils.h"
16 #include "chrome/common/json_value_serializer.h" 18 #include "chrome/common/json_value_serializer.h"
17 #include "chrome/common/notification_service.h" 19 #include "chrome/common/notification_service.h"
18 #include "chrome/common/url_constants.h" 20 #include "chrome/common/url_constants.h"
19 #include "chrome/common/zip.h" 21 #include "chrome/common/zip.h"
20 #include "third_party/skia/include/core/SkBitmap.h" 22 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "webkit/glue/image_decoder.h" 23 #include "webkit/glue/image_decoder.h"
22 24
23 namespace { 25 namespace {
26 const char kCurrentVersionFileName[] = "Current Version";
27
24 // The name of a temporary directory to install an extension into for 28 // The name of a temporary directory to install an extension into for
25 // validation before finalizing install. 29 // validation before finalizing install.
26 const char kTempExtensionName[] = "TEMP_INSTALL"; 30 const char kTempExtensionName[] = "TEMP_INSTALL";
27 31
28 // The file to write our decoded images to, relative to the extension_path. 32 // The file to write our decoded images to, relative to the extension_path.
29 const char kDecodedImagesFilename[] = "DECODED_IMAGES"; 33 const char kDecodedImagesFilename[] = "DECODED_IMAGES";
30 34
31 // Errors 35 // Chromium Extension magic number
32 const char* kCouldNotCreateDirectoryError = 36 // TODO(aa): This should use the one in ExtensionCreator once we transition this
33 "Could not create directory for unzipping."; 37 // to ouptut the same format.
34 const char* kCouldNotDecodeImageError = "Could not decode theme image."; 38 const char kExtensionFileMagic[] = "Cr24";
35 const char* kCouldNotUnzipExtension = "Could not unzip extension.";
36 const char* kPathNamesMustBeAbsoluteOrLocalError =
37 "Path names must not be absolute or contain '..'.";
38 39
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;
39 } // namespace 56 } // namespace
40 57
41 static SkBitmap DecodeImage(const FilePath& path) { 58 static SkBitmap DecodeImage(const FilePath& path) {
42 // Read the file from disk. 59 // Read the file from disk.
43 std::string file_contents; 60 std::string file_contents;
44 if (!file_util::PathExists(path) || 61 if (!file_util::PathExists(path) ||
45 !file_util::ReadFileToString(path, &file_contents)) { 62 !file_util::ReadFileToString(path, &file_contents)) {
46 return SkBitmap(); 63 return SkBitmap();
47 } 64 }
48 65
(...skipping 17 matching lines...) Expand all
66 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) { 83 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
67 return true; 84 return true;
68 } 85 }
69 ++i; 86 ++i;
70 } 87 }
71 } 88 }
72 89
73 return false; 90 return false;
74 } 91 }
75 92
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
76 DictionaryValue* ExtensionUnpacker::ReadManifest() { 161 DictionaryValue* ExtensionUnpacker::ReadManifest() {
77 FilePath manifest_path = 162 FilePath manifest_path =
78 temp_install_dir_.AppendASCII(Extension::kManifestFilename); 163 temp_install_dir_.AppendASCII(Extension::kManifestFilename);
79 if (!file_util::PathExists(manifest_path)) { 164 if (!file_util::PathExists(manifest_path)) {
80 SetError(Extension::kInvalidManifestError); 165 SetError(Extension::kInvalidManifestError);
81 return NULL; 166 return NULL;
82 } 167 }
83 168
84 JSONFileValueSerializer serializer(manifest_path); 169 JSONFileValueSerializer serializer(manifest_path);
85 std::string error; 170 std::string error;
86 scoped_ptr<Value> root(serializer.Deserialize(&error)); 171 scoped_ptr<Value> root(serializer.Deserialize(&error));
87 if (!root.get()) { 172 if (!root.get()) {
88 SetError(error); 173 SetError(error);
89 return NULL; 174 return NULL;
90 } 175 }
91 176
92 if (!root->IsType(Value::TYPE_DICTIONARY)) { 177 if (!root->IsType(Value::TYPE_DICTIONARY)) {
93 SetError(Extension::kInvalidManifestError); 178 SetError(Extension::kInvalidManifestError);
94 return NULL; 179 return NULL;
95 } 180 }
96 181
97 return static_cast<DictionaryValue*>(root.release()); 182 return static_cast<DictionaryValue*>(root.release());
98 } 183 }
99 184
100 bool ExtensionUnpacker::Run() { 185 bool ExtensionUnpacker::Run() {
101 LOG(INFO) << "Installing extension " << extension_path_.value(); 186 LOG(INFO) << "Installing extension " << extension_path_.value();
102 187
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
103 // <profile>/Extensions/INSTALL_TEMP/<version> 206 // <profile>/Extensions/INSTALL_TEMP/<version>
104 temp_install_dir_ = 207 temp_install_dir_ =
105 extension_path_.DirName().AppendASCII(kTempExtensionName); 208 extension_path_.DirName().AppendASCII(kTempExtensionName);
106 if (!file_util::CreateDirectory(temp_install_dir_)) { 209 if (!file_util::CreateDirectory(temp_install_dir_)) {
107 SetError(kCouldNotCreateDirectoryError); 210 SetError("Couldn't create directory for unzipping.");
108 return false; 211 return false;
109 } 212 }
110 213
111 if (!Unzip(extension_path_, temp_install_dir_)) { 214 if (!Unzip(extension_path_, temp_install_dir_)) {
112 SetError(kCouldNotUnzipExtension); 215 SetError("Couldn't unzip extension.");
113 return false; 216 return false;
114 } 217 }
115 218
116 // Parse the manifest. 219 // Parse the manifest.
117 parsed_manifest_.reset(ReadManifest()); 220 parsed_manifest_.reset(ReadManifest());
118 if (!parsed_manifest_.get()) 221 if (!parsed_manifest_.get())
119 return false; // Error was already reported. 222 return false; // Error was already reported.
120 223
121 // NOTE: Since the Unpacker doesn't have the extension's public_id, the 224 // Re-read the actual manifest into our extension struct.
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.
125 Extension extension; 225 Extension extension;
126 std::string error;
127 if (!extension.InitFromValue(*parsed_manifest_, 226 if (!extension.InitFromValue(*parsed_manifest_,
128 false, 227 true, // require ID
129 &error)) { 228 &error)) {
130 SetError(error); 229 SetError(error);
131 return false; 230 return false;
132 } 231 }
232
133 // Decode any images that the browser needs to display. 233 // Decode any images that the browser needs to display.
134 std::set<FilePath> image_paths = extension.GetBrowserImages(); 234 std::set<FilePath> image_paths = extension.GetBrowserImages();
135 for (std::set<FilePath>::iterator it = image_paths.begin(); 235 for (std::set<FilePath>::iterator it = image_paths.begin();
136 it != image_paths.end(); ++it) { 236 it != image_paths.end(); ++it) {
137 if (!AddDecodedImage(*it)) 237 if (!AddDecodedImage(*it))
138 return false; // Error was already reported. 238 return false; // Error was already reported.
139 } 239 }
140 240
141 return true; 241 return true;
142 } 242 }
(...skipping 21 matching lines...) Expand all
164 return false; 264 return false;
165 265
166 IPC::Message pickle(file_str.data(), file_str.size()); 266 IPC::Message pickle(file_str.data(), file_str.size());
167 void* iter = NULL; 267 void* iter = NULL;
168 return IPC::ReadParam(&pickle, &iter, images); 268 return IPC::ReadParam(&pickle, &iter, images);
169 } 269 }
170 270
171 bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) { 271 bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
172 // Make sure it's not referencing a file outside the extension's subdir. 272 // Make sure it's not referencing a file outside the extension's subdir.
173 if (path.IsAbsolute() || PathContainsParentDirectory(path)) { 273 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
174 SetError(kPathNamesMustBeAbsoluteOrLocalError); 274 SetError("Path names must not be absolute or contain '..'.");
175 return false; 275 return false;
176 } 276 }
177 277
178 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path)); 278 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
179 if (image_bitmap.isNull()) { 279 if (image_bitmap.isNull()) {
180 SetError(kCouldNotDecodeImageError); 280 SetError("Could not decode theme image.");
181 return false; 281 return false;
182 } 282 }
183 283
184 decoded_images_.push_back(MakeTuple(image_bitmap, path)); 284 decoded_images_.push_back(MakeTuple(image_bitmap, path));
185 return true; 285 return true;
186 } 286 }
187 287
188 void ExtensionUnpacker::SetError(const std::string &error) { 288 void ExtensionUnpacker::SetError(const std::string &error) {
189 error_message_ = error; 289 error_message_ = error;
190 } 290 }
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