diff options
author | Sen Jiang <senj@google.com> | 2015-09-24 17:30:50 -0700 |
---|---|---|
committer | Sen Jiang <senj@google.com> | 2015-10-05 15:41:51 -0700 |
commit | b8060e435f0edb8efef4891a99fa18f642e01aa2 (patch) | |
tree | 03b651f537ac25203882f32610d5ebe8c3119121 | |
parent | 981eb1151f42480ea10cb5283ab2108454a16c92 (diff) | |
download | update_engine-b8060e435f0edb8efef4891a99fa18f642e01aa2.tar.gz |
Parse metadata signature size in payload version 2.
This patch only parse the field and skip the signature without verifying it.
Bug: 23946683
TEST=unit test added.
Change-Id: I53e049c35f8c21d325aeb415ac9a2daf980fcda1
-rw-r--r-- | delta_performer.cc | 121 | ||||
-rw-r--r-- | delta_performer.h | 38 | ||||
-rw-r--r-- | delta_performer_unittest.cc | 35 | ||||
-rw-r--r-- | payload_constants.cc | 2 | ||||
-rw-r--r-- | payload_constants.h | 2 | ||||
-rw-r--r-- | payload_generator/payload_file.cc | 4 |
6 files changed, 148 insertions, 54 deletions
diff --git a/delta_performer.cc b/delta_performer.cc index c8a00d30..4025b1fe 100644 --- a/delta_performer.cc +++ b/delta_performer.cc @@ -56,8 +56,13 @@ using std::vector; namespace chromeos_update_engine { +const uint64_t DeltaPerformer::kDeltaVersionOffset = sizeof(kDeltaMagic); const uint64_t DeltaPerformer::kDeltaVersionSize = 8; +const uint64_t DeltaPerformer::kDeltaManifestSizeOffset = + kDeltaVersionOffset + kDeltaVersionSize; const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8; +const uint64_t DeltaPerformer::kDeltaMetadataSignatureSizeSize = 4; +const uint64_t DeltaPerformer::kMaxPayloadHeaderSize = 24; const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 1; const uint64_t DeltaPerformer::kSupportedMinorPayloadVersion = 2; @@ -327,25 +332,39 @@ void LogPartitionInfo(const DeltaArchiveManifest& manifest) { } // namespace -uint64_t DeltaPerformer::GetVersionOffset() { - // Manifest size is stored right after the magic string and the version. - return strlen(kDeltaMagic); -} - -uint64_t DeltaPerformer::GetManifestSizeOffset() { - // Manifest size is stored right after the magic string and the version. - return strlen(kDeltaMagic) + kDeltaVersionSize; +bool DeltaPerformer::GetMetadataSignatureSizeOffset( + uint64_t* out_offset) const { + if (GetMajorVersion() == kBrilloMajorPayloadVersion) { + *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize; + return true; + } + return false; } -uint64_t DeltaPerformer::GetManifestOffset() { - // Actual manifest begins right after the manifest size field. - return GetManifestSizeOffset() + kDeltaManifestSizeSize; +bool DeltaPerformer::GetManifestOffset(uint64_t* out_offset) const { + // Actual manifest begins right after the manifest size field or + // metadata signature size field if major version >= 2. + if (major_payload_version_ == kChromeOSMajorPayloadVersion) { + *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize; + return true; + } + if (major_payload_version_ == kBrilloMajorPayloadVersion) { + *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize + + kDeltaMetadataSignatureSizeSize; + return true; + } + LOG(ERROR) << "Unknown major payload version: " << major_payload_version_; + return false; } uint64_t DeltaPerformer::GetMetadataSize() const { return metadata_size_; } +uint64_t DeltaPerformer::GetMajorVersion() const { + return major_payload_version_; +} + uint32_t DeltaPerformer::GetMinorVersion() const { if (manifest_.has_minor_version()) { return manifest_.minor_version(); @@ -363,56 +382,82 @@ bool DeltaPerformer::GetManifest(DeltaArchiveManifest* out_manifest_p) const { return true; } +bool DeltaPerformer::IsHeaderParsed() const { + return metadata_size_ != 0; +} DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata( const chromeos::Blob& payload, ErrorCode* error) { *error = ErrorCode::kSuccess; - const uint64_t manifest_offset = GetManifestOffset(); - uint64_t manifest_size = (metadata_size_ ? - metadata_size_ - manifest_offset : 0); + uint64_t manifest_offset; - if (!manifest_size) { - // Ensure we have data to cover the payload header. - if (payload.size() < manifest_offset) + if (!IsHeaderParsed()) { + // Ensure we have data to cover the major payload version. + if (payload.size() < kDeltaManifestSizeOffset) return kMetadataParseInsufficientData; // Validate the magic string. - if (memcmp(payload.data(), kDeltaMagic, strlen(kDeltaMagic)) != 0) { + if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) { LOG(ERROR) << "Bad payload format -- invalid delta magic."; *error = ErrorCode::kDownloadInvalidMetadataMagicString; return kMetadataParseError; } // Extract the payload version from the metadata. - uint64_t major_payload_version; - COMPILE_ASSERT(sizeof(major_payload_version) == kDeltaVersionSize, + COMPILE_ASSERT(sizeof(major_payload_version_) == kDeltaVersionSize, major_payload_version_size_mismatch); - memcpy(&major_payload_version, - &payload[GetVersionOffset()], + memcpy(&major_payload_version_, + &payload[kDeltaVersionOffset], kDeltaVersionSize); // switch big endian to host - major_payload_version = be64toh(major_payload_version); + major_payload_version_ = be64toh(major_payload_version_); - if (major_payload_version != kSupportedMajorPayloadVersion) { + if (major_payload_version_ != supported_major_version_) { LOG(ERROR) << "Bad payload format -- unsupported payload version: " - << major_payload_version; + << major_payload_version_; + *error = ErrorCode::kUnsupportedMajorPayloadVersion; + return kMetadataParseError; + } + + // Get the manifest offset now that we have payload version. + if (!GetManifestOffset(&manifest_offset)) { *error = ErrorCode::kUnsupportedMajorPayloadVersion; return kMetadataParseError; } + // Check again with the manifest offset. + if (payload.size() < manifest_offset) + return kMetadataParseInsufficientData; // Next, parse the manifest size. - COMPILE_ASSERT(sizeof(manifest_size) == kDeltaManifestSizeSize, + COMPILE_ASSERT(sizeof(manifest_size_) == kDeltaManifestSizeSize, manifest_size_size_mismatch); - memcpy(&manifest_size, - &payload[GetManifestSizeOffset()], + memcpy(&manifest_size_, + &payload[kDeltaManifestSizeOffset], kDeltaManifestSizeSize); - manifest_size = be64toh(manifest_size); // switch big endian to host + manifest_size_ = be64toh(manifest_size_); // switch big endian to host + + uint32_t metadata_signature_size = 0; + if (GetMajorVersion() == kBrilloMajorPayloadVersion) { + // Parse the metadata signature size. + COMPILE_ASSERT(sizeof(metadata_signature_size) == + kDeltaMetadataSignatureSizeSize, + metadata_signature_size_size_mismatch); + uint64_t metadata_signature_size_offset; + if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) { + *error = ErrorCode::kError; + return kMetadataParseError; + } + memcpy(&metadata_signature_size, + &payload[metadata_signature_size_offset], + kDeltaMetadataSignatureSizeSize); + metadata_signature_size = be32toh(metadata_signature_size); + } // If the metadata size is present in install plan, check for it immediately // even before waiting for that many number of bytes to be downloaded in the // payload. This will prevent any attack which relies on us downloading data // beyond the expected metadata size. - metadata_size_ = manifest_offset + manifest_size; + metadata_size_ = manifest_offset + manifest_size_ + metadata_signature_size; if (install_plan_->hash_checks_mandatory) { if (install_plan_->metadata_size != metadata_size_) { LOG(ERROR) << "Mandatory metadata size in Omaha response (" @@ -460,8 +505,12 @@ DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata( *error = ErrorCode::kSuccess; } + if (!GetManifestOffset(&manifest_offset)) { + *error = ErrorCode::kUnsupportedMajorPayloadVersion; + return kMetadataParseError; + } // The payload metadata is deemed valid, it's safe to parse the protobuf. - if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size)) { + if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_)) { LOG(ERROR) << "Unable to parse manifest in update file."; *error = ErrorCode::kDownloadManifestParseError; return kMetadataParseError; @@ -485,11 +534,11 @@ bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode *error) { UpdateOverallProgress(false, "Completed "); while (!manifest_valid_) { - // Read data up to the needed limit; this is either the payload header size, - // or the full metadata size (once it becomes known). - const bool do_read_header = !metadata_size_; + // Read data up to the needed limit; this is either maximium payload header + // size, or the full metadata size (once it becomes known). + const bool do_read_header = !IsHeaderParsed(); CopyDataToBuffer(&c_bytes, &count, - (do_read_header ? GetManifestOffset() : + (do_read_header ? kMaxPayloadHeaderSize : metadata_size_)); MetadataParseResult result = ParsePayloadMetadata(buffer_, error); @@ -497,7 +546,7 @@ bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode *error) { return false; if (result == kMetadataParseInsufficientData) { // If we just processed the header, make an attempt on the manifest. - if (do_read_header && metadata_size_) + if (do_read_header && IsHeaderParsed()) continue; return true; diff --git a/delta_performer.h b/delta_performer.h index 087c2adf..83451742 100644 --- a/delta_performer.h +++ b/delta_performer.h @@ -50,8 +50,12 @@ class DeltaPerformer : public FileWriter { kMetadataParseInsufficientData, }; + static const uint64_t kDeltaVersionOffset; static const uint64_t kDeltaVersionSize; + static const uint64_t kDeltaManifestSizeOffset; static const uint64_t kDeltaManifestSizeSize; + static const uint64_t kDeltaMetadataSignatureSizeSize; + static const uint64_t kMaxPayloadHeaderSize; static const uint64_t kSupportedMajorPayloadVersion; static const uint64_t kSupportedMinorPayloadVersion; @@ -81,6 +85,8 @@ class DeltaPerformer : public FileWriter { manifest_parsed_(false), manifest_valid_(false), metadata_size_(0), + manifest_size_(0), + major_payload_version_(0), next_operation_num_(0), buffer_offset_(0), last_updated_buffer_offset_(kuint64max), @@ -93,6 +99,7 @@ class DeltaPerformer : public FileWriter { last_progress_chunk_(0), forced_progress_log_wait_( base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)), + supported_major_version_(kSupportedMajorPayloadVersion), supported_minor_version_(kSupportedMinorPayloadVersion) {} // Opens the kernel. Should be called before or after Open(), but before @@ -186,26 +193,30 @@ class DeltaPerformer : public FileWriter { public_key_path_ = public_key_path; } - // Returns the byte offset at which the payload version can be found. - static uint64_t GetVersionOffset(); + // Set |*out_offset| to the byte offset where the size of the metadata signature + // is stored in a payload. Return true on success, if this field is not + // present in the payload, return false. + bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const; - // Returns the byte offset where the size of the manifest is stored in - // a payload. This offset precedes the actual start of the manifest - // that's returned by the GetManifestOffset method. - static uint64_t GetManifestSizeOffset(); - - // Returns the byte offset at which the manifest protobuf begins in a - // payload. - static uint64_t GetManifestOffset(); + // Set |*out_offset| to the byte offset at which the manifest protobuf begins + // in a payload. Return true on success, false if the offset is unknown. + bool GetManifestOffset(uint64_t* out_offset) const; // Returns the size of the payload metadata, which includes the payload header - // and the manifest. Is the header was not yet parsed, returns zero. + // and the manifest. If the header was not yet parsed, returns zero. uint64_t GetMetadataSize() const; // If the manifest was successfully parsed, copies it to |*out_manifest_p|. // Returns true on success. bool GetManifest(DeltaArchiveManifest* out_manifest_p) const; + // Return true if header parsing is finished and no errors occurred. + bool IsHeaderParsed() const; + + // Returns the major payload version. If the version was not yet parsed, + // returns zero. + uint64_t GetMajorVersion() const; + // Returns the delta minor version. If this value is defined in the manifest, // it returns that value, otherwise it returns the default value. uint32_t GetMinorVersion() const; @@ -331,6 +342,8 @@ class DeltaPerformer : public FileWriter { bool manifest_parsed_; bool manifest_valid_; uint64_t metadata_size_; + uint64_t manifest_size_; + uint64_t major_payload_version_; // Index of the next operation to perform in the manifest. size_t next_operation_num_; @@ -380,6 +393,9 @@ class DeltaPerformer : public FileWriter { const base::TimeDelta forced_progress_log_wait_; base::Time forced_progress_log_time_; + // The payload major payload version supported by DeltaPerformer. + uint64_t supported_major_version_; + // The delta minor payload version supported by DeltaPerformer. uint32_t supported_minor_version_; diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc index 839f2536..d2ec5b02 100644 --- a/delta_performer_unittest.cc +++ b/delta_performer_unittest.cc @@ -260,6 +260,9 @@ class DeltaPerformerTest : public ::testing::Test { EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize()); } + void SetSupportedMajorVersion(uint64_t major_version) { + performer_.supported_major_version_ = major_version; + } FakePrefs prefs_; InstallPlan install_plan_; FakeSystemState fake_system_state_; @@ -460,11 +463,38 @@ TEST_F(DeltaPerformerTest, ValidateManifestBadMinorVersion) { ErrorCode::kUnsupportedMinorPayloadVersion); } +TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) { + SetSupportedMajorVersion(kBrilloMajorPayloadVersion); + EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0)); + EXPECT_TRUE(performer_.OpenKernel("/dev/null")); + EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic))); + + uint64_t major_version = htobe64(kBrilloMajorPayloadVersion); + EXPECT_TRUE(performer_.Write(&major_version, 8)); + + uint64_t manifest_size = rand() % 256; + uint64_t manifest_size_be = htobe64(manifest_size); + EXPECT_TRUE(performer_.Write(&manifest_size_be, 8)); + + uint32_t metadata_signature_size = rand() % 256; + uint32_t metadata_signature_size_be = htobe32(metadata_signature_size); + EXPECT_TRUE(performer_.Write(&metadata_signature_size_be, 4)); + + EXPECT_LT(performer_.Close(), 0); + + EXPECT_TRUE(performer_.IsHeaderParsed()); + EXPECT_EQ(kBrilloMajorPayloadVersion, performer_.GetMajorVersion()); + uint64_t manifest_offset; + EXPECT_TRUE(performer_.GetManifestOffset(&manifest_offset)); + EXPECT_EQ(24, manifest_offset); // 4 + 8 + 8 + 4 + EXPECT_EQ(24 + manifest_size + metadata_signature_size, + performer_.GetMetadataSize()); +} + TEST_F(DeltaPerformerTest, BadDeltaMagicTest) { EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0)); EXPECT_TRUE(performer_.OpenKernel("/dev/null")); EXPECT_TRUE(performer_.Write("junk", 4)); - EXPECT_TRUE(performer_.Write("morejunk", 8)); EXPECT_FALSE(performer_.Write("morejunk", 8)); EXPECT_LT(performer_.Close(), 0); } @@ -476,10 +506,9 @@ TEST_F(DeltaPerformerTest, WriteUpdatesPayloadState) { EXPECT_CALL(*(fake_system_state_.mock_payload_state()), DownloadProgress(4)).Times(1); EXPECT_CALL(*(fake_system_state_.mock_payload_state()), - DownloadProgress(8)).Times(2); + DownloadProgress(8)).Times(1); EXPECT_TRUE(performer_.Write("junk", 4)); - EXPECT_TRUE(performer_.Write("morejunk", 8)); EXPECT_FALSE(performer_.Write("morejunk", 8)); EXPECT_LT(performer_.Close(), 0); } diff --git a/payload_constants.cc b/payload_constants.cc index b28461b9..cc184309 100644 --- a/payload_constants.cc +++ b/payload_constants.cc @@ -28,7 +28,7 @@ const uint32_t kSourceMinorPayloadVersion = 2; const char kLegacyPartitionNameKernel[] = "boot"; const char kLegacyPartitionNameRoot[] = "system"; -const char kDeltaMagic[] = "CrAU"; +const char kDeltaMagic[4] = {'C', 'r', 'A', 'U'}; const char kBspatchPath[] = "bspatch"; const char* InstallOperationTypeName(InstallOperation_Type op_type) { diff --git a/payload_constants.h b/payload_constants.h index 81bc36af..38cc075e 100644 --- a/payload_constants.h +++ b/payload_constants.h @@ -48,7 +48,7 @@ extern const char kLegacyPartitionNameKernel[]; extern const char kLegacyPartitionNameRoot[]; extern const char kBspatchPath[]; -extern const char kDeltaMagic[]; +extern const char kDeltaMagic[4]; // A block number denoting a hole on a sparse file. Used on Extents to refer to // section of blocks not present on disk on a sparse file. diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc index f0a66cc6..cdcc967b 100644 --- a/payload_generator/payload_file.cc +++ b/payload_generator/payload_file.cc @@ -191,7 +191,7 @@ bool PayloadFile::WritePayload(const string& payload_file, ScopedFileWriterCloser writer_closer(&writer); // Write header - TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic))); + TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, sizeof(kDeltaMagic))); // Write major version number TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, major_version_)); @@ -241,7 +241,7 @@ bool PayloadFile::WritePayload(const string& payload_file, } *medatata_size_out = - strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size(); + sizeof(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size(); ReportPayloadUsage(*medatata_size_out); return true; } |