diff options
author | Sami Tolvanen <samitolvanen@google.com> | 2016-06-03 13:56:07 -0700 |
---|---|---|
committer | Sami Tolvanen <samitolvanen@google.com> | 2016-06-06 13:51:23 -0700 |
commit | 65cbaeb020b209f3d75d594ebbe49a609dd3c7e7 (patch) | |
tree | 664130c5b953ecc77ec6f612c440d128873665a0 | |
parent | 871e63d9b7e584bc398dd4aa983cf561a5e8394e (diff) | |
download | extras-65cbaeb020b209f3d75d594ebbe49a609dd3c7e7.tar.gz |
libfec: return raw and corrected verity metadata signatures
Since it's not possible for libfec to identify whether the signature
field was successfully corrected, return both and allow the caller to
validate either signature.
Bug: 28943429
Change-Id: Ie913c21ba1d07d6df4c6feeb7226b2ec963f4d19
-rw-r--r-- | libfec/fec_open.cpp | 5 | ||||
-rw-r--r-- | libfec/fec_private.h | 1 | ||||
-rw-r--r-- | libfec/fec_verity.cpp | 103 | ||||
-rw-r--r-- | libfec/include/fec/io.h | 1 |
4 files changed, 79 insertions, 31 deletions
diff --git a/libfec/fec_open.cpp b/libfec/fec_open.cpp index c1d4afdb..0e41bf4f 100644 --- a/libfec/fec_open.cpp +++ b/libfec/fec_open.cpp @@ -460,7 +460,10 @@ int fec_verity_get_metadata(struct fec_handle *f, struct fec_verity_metadata *da data->disabled = f->verity.disabled; data->data_size = f->data_size; - memcpy(data->signature, f->verity.header.signature, sizeof(data->signature)); + memcpy(data->signature, f->verity.header.signature, + sizeof(data->signature)); + memcpy(data->ecc_signature, f->verity.ecc_header.signature, + sizeof(data->ecc_signature)); data->table = f->verity.table; data->table_length = f->verity.header.length; diff --git a/libfec/fec_private.h b/libfec/fec_private.h index bde30bd8..238c4e2e 100644 --- a/libfec/fec_private.h +++ b/libfec/fec_private.h @@ -88,6 +88,7 @@ struct verity_info { uint64_t metadata_start; /* offset in file */ uint8_t zero_hash[SHA256_DIGEST_LENGTH]; verity_header header; + verity_header ecc_header; }; struct verity_block_info { diff --git a/libfec/fec_verity.cpp b/libfec/fec_verity.cpp index ca4572c6..393e9624 100644 --- a/libfec/fec_verity.cpp +++ b/libfec/fec_verity.cpp @@ -481,6 +481,45 @@ static int rewrite_metadata(fec_handle *f, uint64_t offset) return raw_pwrite(f, metadata.get(), VERITY_METADATA_SIZE, offset); } +static int validate_header(const fec_handle *f, const verity_header *header, + uint64_t offset) +{ + check(f); + check(header); + + if (header->magic != VERITY_MAGIC && + header->magic != VERITY_MAGIC_DISABLE) { + return -1; + } + + if (header->version != VERITY_VERSION) { + error("unsupported verity version %u", header->version); + return -1; + } + + if (header->length < VERITY_MIN_TABLE_SIZE || + header->length > VERITY_MAX_TABLE_SIZE) { + error("invalid verity table size: %u; expected [" + stringify(VERITY_MIN_TABLE_SIZE) ", " + stringify(VERITY_MAX_TABLE_SIZE) ")", header->length); + return -1; + } + + /* signature is skipped, because for our purposes it won't matter from + where the data originates; the caller of the library is responsible + for signature verification */ + + if (offset > UINT64_MAX - header->length) { + error("invalid verity table length: %u", header->length); + return -1; + } else if (offset + header->length >= f->data_size) { + error("invalid verity table length: %u", header->length); + return -1; + } + + return 0; +} + /* attempts to read verity metadata from `f->fd' position `offset'; if in r/w mode, rewrites the metadata if it had errors */ int verity_parse_header(fec_handle *f, uint64_t offset) @@ -497,55 +536,59 @@ int verity_parse_header(fec_handle *f, uint64_t offset) verity_info *v = &f->verity; uint64_t errors = f->errors; - if (fec_pread(f, &v->header, sizeof(v->header), offset) != - sizeof(v->header)) { + if (!raw_pread(f, &v->header, sizeof(v->header), offset)) { error("failed to read verity header: %s", strerror(errno)); return -1; } - verity_header raw_header; - - if (!raw_pread(f, &raw_header, sizeof(raw_header), offset)) { - error("failed to read verity header: %s", strerror(errno)); - return -1; - } /* use raw data to check for the alternative magic, because it will be error corrected to VERITY_MAGIC otherwise */ - if (raw_header.magic == VERITY_MAGIC_DISABLE) { + if (v->header.magic == VERITY_MAGIC_DISABLE) { /* this value is not used by us, but can be used by a caller to decide whether dm-verity should be enabled */ v->disabled = true; - } else if (v->header.magic != VERITY_MAGIC) { - return -1; } - if (v->header.version != VERITY_VERSION) { - error("unsupported verity version %u", v->header.version); + if (fec_pread(f, &v->ecc_header, sizeof(v->ecc_header), offset) != + sizeof(v->ecc_header)) { + warn("failed to read verity header: %s", strerror(errno)); return -1; } - if (v->header.length < VERITY_MIN_TABLE_SIZE || - v->header.length > VERITY_MAX_TABLE_SIZE) { - error("invalid verity table size: %u; expected [" - stringify(VERITY_MIN_TABLE_SIZE) ", " - stringify(VERITY_MAX_TABLE_SIZE) ")", v->header.length); - return -1; - } + if (validate_header(f, &v->header, offset)) { + /* raw verity header is invalid; this could be due to corruption, or + due to missing verity metadata */ - v->metadata_start = offset; + if (validate_header(f, &v->ecc_header, offset)) { + return -1; /* either way, we cannot recover */ + } - /* signature is skipped, because for our purposes it won't matter from - where the data originates; the caller of the library is responsible - for signature verification */ + /* report mismatching fields */ + if (!v->disabled && v->header.magic != v->ecc_header.magic) { + warn("corrected verity header magic"); + v->header.magic = v->ecc_header.magic; + } - if (offset > UINT64_MAX - v->header.length) { - error("invalid verity table length: %u", v->header.length); - return -1; - } else if (offset + v->header.length >= f->data_size) { - error("invalid verity table length: %u", v->header.length); - return -1; + if (v->header.version != v->ecc_header.version) { + warn("corrected verity header version"); + v->header.version = v->ecc_header.version; + } + + if (v->header.length != v->ecc_header.length) { + warn("corrected verity header length"); + v->header.length = v->ecc_header.length; + } + + if (memcmp(v->header.signature, v->ecc_header.signature, + sizeof(v->header.signature))) { + warn("corrected verity header signature"); + /* we have no way of knowing which signature is correct, if either + of them is */ + } } + v->metadata_start = offset; + if (parse_table(f, offset + sizeof(v->header), v->header.length) == -1) { return -1; } diff --git a/libfec/include/fec/io.h b/libfec/include/fec/io.h index 1a077f32..3b5dac06 100644 --- a/libfec/include/fec/io.h +++ b/libfec/include/fec/io.h @@ -71,6 +71,7 @@ struct fec_verity_metadata { bool disabled; uint64_t data_size; uint8_t signature[RSANUMBYTES]; + uint8_t ecc_signature[RSANUMBYTES]; const char *table; uint32_t table_length; }; |