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

Side by Side Diff: delta_performer.cc

Issue 3592008: AU: Verify delta payload signature and signed hash. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/update_engine.git
Patch Set: move /tmp files to /var/run Created 10 years, 2 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 | Annotate | Revision Log
« no previous file with comments | « delta_performer.h ('k') | delta_performer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium OS 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 "update_engine/delta_performer.h" 5 #include "update_engine/delta_performer.h"
6
6 #include <endian.h> 7 #include <endian.h>
7 #include <errno.h> 8 #include <errno.h>
9
8 #include <algorithm> 10 #include <algorithm>
9 #include <cstring> 11 #include <cstring>
10 #include <string> 12 #include <string>
11 #include <vector> 13 #include <vector>
12 14
15 #include <base/scoped_ptr.h>
16 #include <base/string_util.h>
13 #include <google/protobuf/repeated_field.h> 17 #include <google/protobuf/repeated_field.h>
14 18
15 #include "base/scoped_ptr.h"
16 #include "base/string_util.h"
17 #include "update_engine/bzip_extent_writer.h" 19 #include "update_engine/bzip_extent_writer.h"
18 #include "update_engine/delta_diff_generator.h" 20 #include "update_engine/delta_diff_generator.h"
19 #include "update_engine/extent_writer.h" 21 #include "update_engine/extent_writer.h"
20 #include "update_engine/graph_types.h" 22 #include "update_engine/graph_types.h"
23 #include "update_engine/payload_signer.h"
21 #include "update_engine/subprocess.h" 24 #include "update_engine/subprocess.h"
22 25
23 using std::min; 26 using std::min;
24 using std::string; 27 using std::string;
25 using std::vector; 28 using std::vector;
26 using google::protobuf::RepeatedPtrField; 29 using google::protobuf::RepeatedPtrField;
27 30
28 namespace chromeos_update_engine { 31 namespace chromeos_update_engine {
29 32
30 namespace { 33 namespace {
31 34
32 const int kDeltaVersionLength = 8; 35 const int kDeltaVersionLength = 8;
33 const int kDeltaProtobufLengthLength = 8; 36 const int kDeltaProtobufLengthLength = 8;
34 37 const char kUpdatePayloadPublicKeyPath[] =
35 // Remove count bytes from the beginning of *buffer. 38 "/usr/share/update_engine/update-payload-key.pub.pem";
36 void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) {
37 buffer->erase(buffer->begin(), buffer->begin() + count);
38 }
39 39
40 // Converts extents to a human-readable string, for use by DumpUpdateProto(). 40 // Converts extents to a human-readable string, for use by DumpUpdateProto().
41 string ExtentsToString(const RepeatedPtrField<Extent>& extents) { 41 string ExtentsToString(const RepeatedPtrField<Extent>& extents) {
42 string ret; 42 string ret;
43 for (int i = 0; i < extents.size(); i++) { 43 for (int i = 0; i < extents.size(); i++) {
44 const Extent& extent = extents.Get(i); 44 const Extent& extent = extents.Get(i);
45 if (extent.start_block() == kSparseHole) { 45 if (extent.start_block() == kSparseHole) {
46 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); 46 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks());
47 } else { 47 } else {
48 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", 48 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ",
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 } 129 }
130 int err = 0; 130 int err = 0;
131 if (close(kernel_fd_) == -1) { 131 if (close(kernel_fd_) == -1) {
132 err = errno; 132 err = errno;
133 PLOG(ERROR) << "Unable to close kernel fd:"; 133 PLOG(ERROR) << "Unable to close kernel fd:";
134 } 134 }
135 if (close(fd_) == -1) { 135 if (close(fd_) == -1) {
136 err = errno; 136 err = errno;
137 PLOG(ERROR) << "Unable to close rootfs fd:"; 137 PLOG(ERROR) << "Unable to close rootfs fd:";
138 } 138 }
139 LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash.";
139 fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail. 140 fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail.
140 path_ = ""; 141 path_ = "";
141 return -err; 142 return -err;
142 } 143 }
143 144
144 // Wrapper around write. Returns bytes written on success or 145 // Wrapper around write. Returns bytes written on success or
145 // -errno on error. 146 // -errno on error.
146 // This function performs as many actions as it can, given the amount of 147 // This function performs as many actions as it can, given the amount of
147 // data received thus far. 148 // data received thus far.
148 ssize_t DeltaPerformer::Write(const void* bytes, size_t count) { 149 ssize_t DeltaPerformer::Write(const void* bytes, size_t count) {
(...skipping 20 matching lines...) Expand all
169 } 170 }
170 // We have the full proto buffer in buffer_. Parse it. 171 // We have the full proto buffer in buffer_. Parse it.
171 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + 172 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength +
172 kDeltaProtobufLengthLength; 173 kDeltaProtobufLengthLength;
173 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { 174 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) {
174 LOG(ERROR) << "Unable to parse manifest in update file."; 175 LOG(ERROR) << "Unable to parse manifest in update file.";
175 return -EINVAL; 176 return -EINVAL;
176 } 177 }
177 // Remove protobuf and header info from buffer_, so buffer_ contains 178 // Remove protobuf and header info from buffer_, so buffer_ contains
178 // just data blobs 179 // just data blobs
179 RemoveBufferHeadBytes(&buffer_, 180 DiscardBufferHeadBytes(strlen(kDeltaMagic) +
180 strlen(kDeltaMagic) + 181 kDeltaVersionLength +
181 kDeltaVersionLength + 182 kDeltaProtobufLengthLength + protobuf_length,
182 kDeltaProtobufLengthLength + protobuf_length); 183 true); // do_hash
183 manifest_valid_ = true; 184 manifest_valid_ = true;
184 block_size_ = manifest_.block_size(); 185 block_size_ = manifest_.block_size();
185 } 186 }
186 ssize_t total_operations = manifest_.install_operations_size() + 187 ssize_t total_operations = manifest_.install_operations_size() +
187 manifest_.kernel_install_operations_size(); 188 manifest_.kernel_install_operations_size();
188 while (next_operation_num_ < total_operations) { 189 while (next_operation_num_ < total_operations) {
189 const DeltaArchiveManifest_InstallOperation &op = 190 const DeltaArchiveManifest_InstallOperation &op =
190 next_operation_num_ < manifest_.install_operations_size() ? 191 next_operation_num_ < manifest_.install_operations_size() ?
191 manifest_.install_operations(next_operation_num_) : 192 manifest_.install_operations(next_operation_num_) :
192 manifest_.kernel_install_operations( 193 manifest_.kernel_install_operations(
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 // Move operations don't require any data blob, so they can always 233 // Move operations don't require any data blob, so they can always
233 // be performed 234 // be performed
234 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) 235 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
235 return true; 236 return true;
236 237
237 // See if we have the entire data blob in the buffer 238 // See if we have the entire data blob in the buffer
238 if (operation.data_offset() < buffer_offset_) { 239 if (operation.data_offset() < buffer_offset_) {
239 LOG(ERROR) << "we threw away data it seems?"; 240 LOG(ERROR) << "we threw away data it seems?";
240 return false; 241 return false;
241 } 242 }
242 243
243 return (operation.data_offset() + operation.data_length()) <= 244 return (operation.data_offset() + operation.data_length()) <=
244 (buffer_offset_ + buffer_.size()); 245 (buffer_offset_ + buffer_.size());
245 } 246 }
246 247
247 bool DeltaPerformer::PerformReplaceOperation( 248 bool DeltaPerformer::PerformReplaceOperation(
248 const DeltaArchiveManifest_InstallOperation& operation, 249 const DeltaArchiveManifest_InstallOperation& operation,
249 bool is_kernel_partition) { 250 bool is_kernel_partition) {
250 CHECK(operation.type() == \ 251 CHECK(operation.type() == \
251 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \ 252 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \
252 operation.type() == \ 253 operation.type() == \
253 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); 254 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
254 255
255 // Since we delete data off the beginning of the buffer as we use it, 256 // Since we delete data off the beginning of the buffer as we use it,
256 // the data we need should be exactly at the beginning of the buffer. 257 // the data we need should be exactly at the beginning of the buffer.
257 CHECK_EQ(buffer_offset_, operation.data_offset()); 258 CHECK_EQ(buffer_offset_, operation.data_offset());
258 CHECK_GE(buffer_.size(), operation.data_length()); 259 CHECK_GE(buffer_.size(), operation.data_length());
259 260
261 // Don't include the signature data blob in the hash.
262 bool do_hash = !ExtractSignatureMessage(operation);
263
260 DirectExtentWriter direct_writer; 264 DirectExtentWriter direct_writer;
261 ZeroPadExtentWriter zero_pad_writer(&direct_writer); 265 ZeroPadExtentWriter zero_pad_writer(&direct_writer);
262 scoped_ptr<BzipExtentWriter> bzip_writer; 266 scoped_ptr<BzipExtentWriter> bzip_writer;
263 267
264 // Since bzip decompression is optional, we have a variable writer that will 268 // Since bzip decompression is optional, we have a variable writer that will
265 // point to one of the ExtentWriter objects above. 269 // point to one of the ExtentWriter objects above.
266 ExtentWriter* writer = NULL; 270 ExtentWriter* writer = NULL;
267 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) { 271 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
268 writer = &zero_pad_writer; 272 writer = &zero_pad_writer;
269 } else if (operation.type() == 273 } else if (operation.type() ==
270 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { 274 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
271 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer)); 275 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer));
272 writer = bzip_writer.get(); 276 writer = bzip_writer.get();
273 } else { 277 } else {
274 NOTREACHED(); 278 NOTREACHED();
275 } 279 }
276 280
277 // Create a vector of extents to pass to the ExtentWriter. 281 // Create a vector of extents to pass to the ExtentWriter.
278 vector<Extent> extents; 282 vector<Extent> extents;
279 for (int i = 0; i < operation.dst_extents_size(); i++) { 283 for (int i = 0; i < operation.dst_extents_size(); i++) {
280 extents.push_back(operation.dst_extents(i)); 284 extents.push_back(operation.dst_extents(i));
281 } 285 }
282 286
283 int fd = is_kernel_partition ? kernel_fd_ : fd_; 287 int fd = is_kernel_partition ? kernel_fd_ : fd_;
284 288
285 TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_)); 289 TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_));
286 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); 290 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length()));
287 TEST_AND_RETURN_FALSE(writer->End()); 291 TEST_AND_RETURN_FALSE(writer->End());
288 292
289 // Update buffer 293 // Update buffer
290 buffer_offset_ += operation.data_length(); 294 buffer_offset_ += operation.data_length();
291 RemoveBufferHeadBytes(&buffer_, operation.data_length()); 295 DiscardBufferHeadBytes(operation.data_length(), do_hash);
292 return true; 296 return true;
293 } 297 }
294 298
295 bool DeltaPerformer::PerformMoveOperation( 299 bool DeltaPerformer::PerformMoveOperation(
296 const DeltaArchiveManifest_InstallOperation& operation, 300 const DeltaArchiveManifest_InstallOperation& operation,
297 bool is_kernel_partition) { 301 bool is_kernel_partition) {
298 // Calculate buffer size. Note, this function doesn't do a sliding 302 // Calculate buffer size. Note, this function doesn't do a sliding
299 // window to copy in case the source and destination blocks overlap. 303 // window to copy in case the source and destination blocks overlap.
300 // If we wanted to do a sliding window, we could program the server 304 // If we wanted to do a sliding window, we could program the server
301 // to generate deltas that effectively did a sliding window. 305 // to generate deltas that effectively did a sliding window.
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 (last_extent.start_block() + last_extent.num_blocks()) * block_size_; 428 (last_extent.start_block() + last_extent.num_blocks()) * block_size_;
425 const uint64_t begin_byte = 429 const uint64_t begin_byte =
426 end_byte - (block_size_ - operation.dst_length() % block_size_); 430 end_byte - (block_size_ - operation.dst_length() % block_size_);
427 vector<char> zeros(end_byte - begin_byte); 431 vector<char> zeros(end_byte - begin_byte);
428 TEST_AND_RETURN_FALSE( 432 TEST_AND_RETURN_FALSE(
429 utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte)); 433 utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte));
430 } 434 }
431 435
432 // Update buffer. 436 // Update buffer.
433 buffer_offset_ += operation.data_length(); 437 buffer_offset_ += operation.data_length();
434 RemoveBufferHeadBytes(&buffer_, operation.data_length()); 438 DiscardBufferHeadBytes(operation.data_length(),
439 true); // do_hash
435 return true; 440 return true;
436 } 441 }
437 442
443 bool DeltaPerformer::ExtractSignatureMessage(
444 const DeltaArchiveManifest_InstallOperation& operation) {
445 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
446 !manifest_.has_signatures_offset() ||
447 manifest_.signatures_offset() != operation.data_offset()) {
448 return false;
449 }
450 TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() &&
451 manifest_.signatures_size() == operation.data_length());
452 TEST_AND_RETURN_FALSE(signatures_message_data_.empty());
453 TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset());
454 TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size());
455 signatures_message_data_.insert(
456 signatures_message_data_.begin(),
457 buffer_.begin(),
458 buffer_.begin() + manifest_.signatures_size());
459 LOG(INFO) << "Extracted signature data of size "
460 << manifest_.signatures_size() << " at "
461 << manifest_.signatures_offset();
462 return true;
463 }
464
465 bool DeltaPerformer::VerifyPayload(const string& public_key_path) {
466 string key_path = public_key_path;
467 if (key_path.empty()) {
468 key_path = kUpdatePayloadPublicKeyPath;
469 }
470 LOG(INFO) << "Verifying delta payload. Public key path: " << key_path;
471 if (!utils::FileExists(key_path.c_str())) {
472 LOG(WARNING) << "Not verifying delta payload due to missing public key.";
473 return true;
474 }
475 TEST_AND_RETURN_FALSE(!signatures_message_data_.empty());
476 vector<char> signed_hash_data;
477 TEST_AND_RETURN_FALSE(PayloadSigner::VerifySignature(signatures_message_data_,
478 key_path,
479 &signed_hash_data));
480 const vector<char>& hash_data = hash_calculator_.raw_hash();
481 TEST_AND_RETURN_FALSE(!hash_data.empty());
482 return hash_data == signed_hash_data;
483 }
484
485 void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) {
486 if (do_hash) {
487 hash_calculator_.Update(&buffer_[0], count);
488 }
489 buffer_.erase(buffer_.begin(), buffer_.begin() + count);
490 }
491
438 } // namespace chromeos_update_engine 492 } // namespace chromeos_update_engine
OLDNEW
« no previous file with comments | « delta_performer.h ('k') | delta_performer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698