| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "base/base64.h" | 5 #include "base/base64.h" |
| 6 #include "base/bind.h" | 6 #include "base/bind.h" |
| 7 #include "base/files/file_util.h" | 7 #include "base/files/file_util.h" |
| 8 #include "base/memory/ptr_util.h" | 8 #include "base/memory/ptr_util.h" |
| 9 #include "base/metrics/histogram_macros.h" | 9 #include "base/metrics/histogram_macros.h" |
| 10 #include "base/metrics/sparse_histogram.h" | 10 #include "base/metrics/sparse_histogram.h" |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 state_ = response->new_client_state(); | 204 state_ = response->new_client_state(); |
| 205 return APPLY_UPDATE_SUCCESS; | 205 return APPLY_UPDATE_SUCCESS; |
| 206 } | 206 } |
| 207 | 207 |
| 208 void V4Store::ApplyUpdate( | 208 void V4Store::ApplyUpdate( |
| 209 std::unique_ptr<ListUpdateResponse> response, | 209 std::unique_ptr<ListUpdateResponse> response, |
| 210 const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner, | 210 const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner, |
| 211 UpdatedStoreReadyCallback callback) { | 211 UpdatedStoreReadyCallback callback) { |
| 212 std::unique_ptr<V4Store> new_store( | 212 std::unique_ptr<V4Store> new_store( |
| 213 new V4Store(this->task_runner_, this->store_path_)); | 213 new V4Store(this->task_runner_, this->store_path_)); |
| 214 | |
| 215 ApplyUpdateResult apply_update_result; | 214 ApplyUpdateResult apply_update_result; |
| 216 if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) { | 215 if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) { |
| 217 apply_update_result = new_store->ProcessPartialUpdateAndWriteToDisk( | 216 apply_update_result = new_store->ProcessPartialUpdateAndWriteToDisk( |
| 218 hash_prefix_map_, std::move(response)); | 217 hash_prefix_map_, std::move(response)); |
| 219 } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) { | 218 } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) { |
| 220 apply_update_result = | 219 apply_update_result = |
| 221 new_store->ProcessFullUpdateAndWriteToDisk(std::move(response)); | 220 new_store->ProcessFullUpdateAndWriteToDisk(std::move(response)); |
| 222 } else { | 221 } else { |
| 223 apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE; | 222 apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE; |
| 224 NOTREACHED() << "Unexpected response type: " << response->response_type(); | 223 NOTREACHED() << "Failure: Unexpected response type: " |
| 224 << response->response_type(); |
| 225 } | 225 } |
| 226 | 226 |
| 227 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 227 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
| 228 // new_store is done updating, pass it to the callback. | 228 // new_store is done updating, pass it to the callback. |
| 229 callback_task_runner->PostTask( | 229 callback_task_runner->PostTask( |
| 230 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); | 230 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); |
| 231 } else { | 231 } else { |
| 232 DVLOG(1) << "ApplyUpdate failed: reason: " << apply_update_result | 232 DVLOG(1) << "Failure: ApplyUpdate: reason: " << apply_update_result |
| 233 << "; store: " << *this; | 233 << "; store: " << *this; |
| 234 // new_store failed updating. Pass a nullptr to the callback. | 234 // new_store failed updating. Pass a nullptr to the callback. |
| 235 callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr)); | 235 callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
| 236 } | 236 } |
| 237 | 237 |
| 238 RecordApplyUpdateResult(apply_update_result); | 238 RecordApplyUpdateResult(apply_update_result); |
| 239 } | 239 } |
| 240 | 240 |
| 241 // static | 241 // static |
| 242 ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions( | 242 ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions( |
| 243 const RepeatedPtrField<ThreatEntrySet>& additions, | 243 const RepeatedPtrField<ThreatEntrySet>& additions, |
| 244 HashPrefixMap* additions_map) { | 244 HashPrefixMap* additions_map) { |
| 245 for (const auto& addition : additions) { | 245 for (const auto& addition : additions) { |
| 246 ApplyUpdateResult apply_update_result = APPLY_UPDATE_SUCCESS; | 246 ApplyUpdateResult apply_update_result = APPLY_UPDATE_SUCCESS; |
| 247 const CompressionType compression_type = addition.compression_type(); | 247 const CompressionType compression_type = addition.compression_type(); |
| 248 if (compression_type == RAW) { | 248 if (compression_type == RAW) { |
| 249 DCHECK(addition.has_raw_hashes()); | 249 DCHECK(addition.has_raw_hashes()); |
| 250 DCHECK(addition.raw_hashes().has_raw_hashes()); | 250 DCHECK(addition.raw_hashes().has_raw_hashes()); |
| 251 | 251 |
| 252 apply_update_result = | 252 apply_update_result = |
| 253 AddUnlumpedHashes(addition.raw_hashes().prefix_size(), | 253 AddUnlumpedHashes(addition.raw_hashes().prefix_size(), |
| 254 addition.raw_hashes().raw_hashes(), additions_map); | 254 addition.raw_hashes().raw_hashes(), additions_map); |
| 255 } else if (compression_type == RICE) { | 255 } else if (compression_type == RICE) { |
| 256 DCHECK(addition.has_rice_hashes()); | 256 DCHECK(addition.has_rice_hashes()); |
| 257 | 257 |
| 258 const RiceDeltaEncoding& rice_hashes = addition.rice_hashes(); | 258 const RiceDeltaEncoding& rice_hashes = addition.rice_hashes(); |
| 259 std::string raw_hashes; | 259 std::vector<uint32_t> raw_hashes; |
| 260 V4DecodeResult decode_result = V4RiceDecoder::DecodeBytes( | 260 V4DecodeResult decode_result = V4RiceDecoder::DecodePrefixes( |
| 261 rice_hashes.first_value(), rice_hashes.rice_parameter(), | 261 rice_hashes.first_value(), rice_hashes.rice_parameter(), |
| 262 rice_hashes.num_entries(), rice_hashes.encoded_data(), &raw_hashes); | 262 rice_hashes.num_entries(), rice_hashes.encoded_data(), &raw_hashes); |
| 263 RecordDecodeAdditionsResult(decode_result); | 263 RecordDecodeAdditionsResult(decode_result); |
| 264 if (decode_result != DECODE_SUCCESS) { | 264 if (decode_result != DECODE_SUCCESS) { |
| 265 return RICE_DECODING_FAILURE; | 265 return RICE_DECODING_FAILURE; |
| 266 } else { | 266 } else { |
| 267 char* raw_hashes_start = reinterpret_cast<char*>(raw_hashes.data()); |
| 268 size_t raw_hashes_size = sizeof(uint32_t) * raw_hashes.size(); |
| 269 |
| 267 // Rice-Golomb encoding is used to send compressed compressed 4-byte | 270 // Rice-Golomb encoding is used to send compressed compressed 4-byte |
| 268 // hash prefixes. Hash prefixes longer than 4 bytes will not be | 271 // hash prefixes. Hash prefixes longer than 4 bytes will not be |
| 269 // compressed, and will be served in raw format instead. | 272 // compressed, and will be served in raw format instead. |
| 270 // Source: https://developers.google.com/safe-browsing/v4/compression | 273 // Source: https://developers.google.com/safe-browsing/v4/compression |
| 271 const PrefixSize kPrefixSize = 4; | 274 const PrefixSize kPrefixSize = 4; |
| 272 apply_update_result = | 275 apply_update_result = AddUnlumpedHashes(kPrefixSize, raw_hashes_start, |
| 273 AddUnlumpedHashes(kPrefixSize, raw_hashes, additions_map); | 276 raw_hashes_size, additions_map); |
| 274 } | 277 } |
| 275 } else { | 278 } else { |
| 276 NOTREACHED() << "Unexpected compression_type type: " << compression_type; | 279 NOTREACHED() << "Unexpected compression_type type: " << compression_type; |
| 277 return UNEXPECTED_COMPRESSION_TYPE_ADDITIONS_FAILURE; | 280 return UNEXPECTED_COMPRESSION_TYPE_ADDITIONS_FAILURE; |
| 278 } | 281 } |
| 279 | 282 |
| 280 if (apply_update_result != APPLY_UPDATE_SUCCESS) { | 283 if (apply_update_result != APPLY_UPDATE_SUCCESS) { |
| 281 // If there was an error in updating the map, discard the update entirely. | 284 // If there was an error in updating the map, discard the update entirely. |
| 282 return apply_update_result; | 285 return apply_update_result; |
| 283 } | 286 } |
| 284 } | 287 } |
| 285 | 288 |
| 286 return APPLY_UPDATE_SUCCESS; | 289 return APPLY_UPDATE_SUCCESS; |
| 287 } | 290 } |
| 288 | 291 |
| 289 // static | 292 // static |
| 290 ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size, | 293 ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size, |
| 291 const std::string& lumped_hashes, | 294 const std::string& raw_hashes, |
| 295 HashPrefixMap* additions_map) { |
| 296 return AddUnlumpedHashes(prefix_size, raw_hashes.data(), raw_hashes.size(), |
| 297 additions_map); |
| 298 } |
| 299 |
| 300 // static |
| 301 ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size, |
| 302 const char* raw_hashes_begin, |
| 303 const size_t raw_hashes_length, |
| 292 HashPrefixMap* additions_map) { | 304 HashPrefixMap* additions_map) { |
| 293 if (prefix_size < kMinHashPrefixLength) { | 305 if (prefix_size < kMinHashPrefixLength) { |
| 294 NOTREACHED(); | 306 NOTREACHED(); |
| 295 return PREFIX_SIZE_TOO_SMALL_FAILURE; | 307 return PREFIX_SIZE_TOO_SMALL_FAILURE; |
| 296 } | 308 } |
| 297 if (prefix_size > kMaxHashPrefixLength) { | 309 if (prefix_size > kMaxHashPrefixLength) { |
| 298 NOTREACHED(); | 310 NOTREACHED(); |
| 299 return PREFIX_SIZE_TOO_LARGE_FAILURE; | 311 return PREFIX_SIZE_TOO_LARGE_FAILURE; |
| 300 } | 312 } |
| 301 if (lumped_hashes.size() % prefix_size != 0) { | 313 if (raw_hashes_length % prefix_size != 0) { |
| 302 return ADDITIONS_SIZE_UNEXPECTED_FAILURE; | 314 return ADDITIONS_SIZE_UNEXPECTED_FAILURE; |
| 303 } | 315 } |
| 316 |
| 304 // TODO(vakh): Figure out a way to avoid the following copy operation. | 317 // TODO(vakh): Figure out a way to avoid the following copy operation. |
| 305 (*additions_map)[prefix_size] = lumped_hashes; | 318 (*additions_map)[prefix_size] = |
| 319 std::string(raw_hashes_begin, raw_hashes_begin + raw_hashes_length); |
| 306 return APPLY_UPDATE_SUCCESS; | 320 return APPLY_UPDATE_SUCCESS; |
| 307 } | 321 } |
| 308 | 322 |
| 309 // static | 323 // static |
| 310 bool V4Store::GetNextSmallestUnmergedPrefix( | 324 bool V4Store::GetNextSmallestUnmergedPrefix( |
| 311 const HashPrefixMap& hash_prefix_map, | 325 const HashPrefixMap& hash_prefix_map, |
| 312 const IteratorMap& iterator_map, | 326 const IteratorMap& iterator_map, |
| 313 HashPrefix* smallest_hash_prefix) { | 327 HashPrefix* smallest_hash_prefix) { |
| 314 HashPrefix current_hash_prefix; | 328 HashPrefix current_hash_prefix; |
| 315 bool has_unmerged = false; | 329 bool has_unmerged = false; |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 461 return REMOVALS_INDEX_TOO_LARGE_FAILURE; | 475 return REMOVALS_INDEX_TOO_LARGE_FAILURE; |
| 462 } | 476 } |
| 463 | 477 |
| 464 if (calculate_checksum) { | 478 if (calculate_checksum) { |
| 465 std::string checksum(crypto::kSHA256Length, 0); | 479 std::string checksum(crypto::kSHA256Length, 0); |
| 466 checksum_ctx->Finish(base::string_as_array(&checksum), checksum.size()); | 480 checksum_ctx->Finish(base::string_as_array(&checksum), checksum.size()); |
| 467 if (checksum != expected_checksum) { | 481 if (checksum != expected_checksum) { |
| 468 std::string checksum_base64, expected_checksum_base64; | 482 std::string checksum_base64, expected_checksum_base64; |
| 469 base::Base64Encode(checksum, &checksum_base64); | 483 base::Base64Encode(checksum, &checksum_base64); |
| 470 base::Base64Encode(expected_checksum, &expected_checksum_base64); | 484 base::Base64Encode(expected_checksum, &expected_checksum_base64); |
| 471 DVLOG(1) << "Checksum failed: calculated: " << checksum_base64 | 485 DVLOG(1) << "Failure: Checksum mismatch: calculated: " << checksum_base64 |
| 472 << "expected: " << expected_checksum_base64; | 486 << " expected: " << expected_checksum_base64; |
| 473 return CHECKSUM_MISMATCH_FAILURE; | 487 return CHECKSUM_MISMATCH_FAILURE; |
| 474 } | 488 } |
| 475 } | 489 } |
| 476 | 490 |
| 477 return APPLY_UPDATE_SUCCESS; | 491 return APPLY_UPDATE_SUCCESS; |
| 478 } | 492 } |
| 479 | 493 |
| 480 StoreReadResult V4Store::ReadFromDisk() { | 494 StoreReadResult V4Store::ReadFromDisk() { |
| 481 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 495 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 482 | 496 |
| 483 std::string contents; | 497 std::string contents; |
| 484 bool read_success = base::ReadFileToString(store_path_, &contents); | 498 bool read_success = base::ReadFileToString(store_path_, &contents); |
| 485 if (!read_success) { | 499 if (!read_success) { |
| 486 return FILE_UNREADABLE_FAILURE; | 500 return FILE_UNREADABLE_FAILURE; |
| 487 } | 501 } |
| 488 | 502 |
| 489 if (contents.empty()) { | 503 if (contents.empty()) { |
| 490 return FILE_EMPTY_FAILURE; | 504 return FILE_EMPTY_FAILURE; |
| 491 } | 505 } |
| 492 | 506 |
| 493 V4StoreFileFormat file_format; | 507 V4StoreFileFormat file_format; |
| 494 if (!file_format.ParseFromString(contents)) { | 508 if (!file_format.ParseFromString(contents)) { |
| 495 return PROTO_PARSING_FAILURE; | 509 return PROTO_PARSING_FAILURE; |
| 496 } | 510 } |
| 497 | 511 |
| 498 if (file_format.magic_number() != kFileMagic) { | 512 if (file_format.magic_number() != kFileMagic) { |
| 499 DVLOG(1) << "Unexpected magic number found in file: " | |
| 500 << file_format.magic_number(); | |
| 501 return UNEXPECTED_MAGIC_NUMBER_FAILURE; | 513 return UNEXPECTED_MAGIC_NUMBER_FAILURE; |
| 502 } | 514 } |
| 503 | 515 |
| 504 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.V4StoreVersionRead", | 516 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.V4StoreVersionRead", |
| 505 file_format.version_number()); | 517 file_format.version_number()); |
| 506 if (file_format.version_number() != kFileVersion) { | 518 if (file_format.version_number() != kFileVersion) { |
| 507 DVLOG(1) << "File version incompatible: " << file_format.version_number() | |
| 508 << "; expected: " << kFileVersion; | |
| 509 return FILE_VERSION_INCOMPATIBLE_FAILURE; | 519 return FILE_VERSION_INCOMPATIBLE_FAILURE; |
| 510 } | 520 } |
| 511 | 521 |
| 512 if (!file_format.has_list_update_response()) { | 522 if (!file_format.has_list_update_response()) { |
| 513 return HASH_PREFIX_INFO_MISSING_FAILURE; | 523 return HASH_PREFIX_INFO_MISSING_FAILURE; |
| 514 } | 524 } |
| 515 | 525 |
| 516 std::unique_ptr<ListUpdateResponse> response(new ListUpdateResponse); | 526 std::unique_ptr<ListUpdateResponse> response(new ListUpdateResponse); |
| 517 response->Swap(file_format.mutable_list_update_response()); | 527 response->Swap(file_format.mutable_list_update_response()); |
| 518 ApplyUpdateResult apply_update_result = ProcessFullUpdate(response); | 528 ApplyUpdateResult apply_update_result = ProcessFullUpdate(response); |
| 519 RecordApplyUpdateResultWhenReadingFromDisk(apply_update_result); | 529 RecordApplyUpdateResultWhenReadingFromDisk(apply_update_result); |
| 520 if (apply_update_result != APPLY_UPDATE_SUCCESS) { | 530 if (apply_update_result != APPLY_UPDATE_SUCCESS) { |
| 521 hash_prefix_map_.clear(); | 531 hash_prefix_map_.clear(); |
| 522 return HASH_PREFIX_MAP_GENERATION_FAILURE; | 532 return HASH_PREFIX_MAP_GENERATION_FAILURE; |
| 523 } | 533 } |
| 524 | 534 |
| 525 return READ_SUCCESS; | 535 return READ_SUCCESS; |
| 526 } | 536 } |
| 527 | 537 |
| 528 StoreWriteResult V4Store::WriteToDisk( | 538 StoreWriteResult V4Store::WriteToDisk( |
| 529 std::unique_ptr<ListUpdateResponse> response) const { | 539 std::unique_ptr<ListUpdateResponse> response) const { |
| 530 // Do not write partial updates to the disk. | 540 // Do not write partial updates to the disk. |
| 531 // After merging the updates, the ListUpdateResponse passed to this method | 541 // After merging the updates, the ListUpdateResponse passed to this method |
| 532 // should be a FULL_UPDATE. | 542 // should be a FULL_UPDATE. |
| 533 if (!response->has_response_type() || | 543 if (!response->has_response_type() || |
| 534 response->response_type() != ListUpdateResponse::FULL_UPDATE) { | 544 response->response_type() != ListUpdateResponse::FULL_UPDATE) { |
| 535 DVLOG(1) << "response->has_response_type(): " | 545 DVLOG(1) << "Failure: response->has_response_type(): " |
| 536 << response->has_response_type(); | 546 << response->has_response_type() |
| 537 DVLOG(1) << "response->response_type(): " << response->response_type(); | 547 << " : response->response_type(): " << response->response_type(); |
| 538 return INVALID_RESPONSE_TYPE_FAILURE; | 548 return INVALID_RESPONSE_TYPE_FAILURE; |
| 539 } | 549 } |
| 540 | 550 |
| 541 // Attempt writing to a temporary file first and at the end, swap the files. | 551 // Attempt writing to a temporary file first and at the end, swap the files. |
| 542 const base::FilePath new_filename = TemporaryFileForFilename(store_path_); | 552 const base::FilePath new_filename = TemporaryFileForFilename(store_path_); |
| 543 | 553 |
| 544 V4StoreFileFormat file_format; | 554 V4StoreFileFormat file_format; |
| 545 file_format.set_magic_number(kFileMagic); | 555 file_format.set_magic_number(kFileMagic); |
| 546 file_format.set_version_number(kFileVersion); | 556 file_format.set_version_number(kFileVersion); |
| 547 ListUpdateResponse* response_to_write = | 557 ListUpdateResponse* response_to_write = |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 594 if (result == 0) { | 604 if (result == 0) { |
| 595 return true; | 605 return true; |
| 596 } else if (result < 0) { | 606 } else if (result < 0) { |
| 597 return HashPrefixMatches(hash_prefix, begin, mid); | 607 return HashPrefixMatches(hash_prefix, begin, mid); |
| 598 } else { | 608 } else { |
| 599 return HashPrefixMatches(hash_prefix, mid + prefix_size, end); | 609 return HashPrefixMatches(hash_prefix, mid + prefix_size, end); |
| 600 } | 610 } |
| 601 } | 611 } |
| 602 | 612 |
| 603 } // namespace safe_browsing | 613 } // namespace safe_browsing |
| OLD | NEW |