aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSen Jiang <senj@google.com>2015-10-05 23:18:23 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-10-05 23:18:23 +0000
commitded6349aec57f2f947ed9858ac4842a491171b12 (patch)
tree28ba2a39ac9723d2729d69085c9d177124c7e894
parent931e49abdbff40e8e471fa85ebba23258c36809b (diff)
parentb8060e435f0edb8efef4891a99fa18f642e01aa2 (diff)
downloadupdate_engine-ded6349aec57f2f947ed9858ac4842a491171b12.tar.gz
Merge "Parse metadata signature size in payload version 2."
-rw-r--r--delta_performer.cc121
-rw-r--r--delta_performer.h38
-rw-r--r--delta_performer_unittest.cc35
-rw-r--r--payload_constants.cc2
-rw-r--r--payload_constants.h2
-rw-r--r--payload_generator/payload_file.cc4
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;
}