blob: 184db6cac9a3a77d158159fe88a5eb02ac679c4c [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/utility/safe_browsing/zip_analyzer.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <set>
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/checked_math.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "components/safe_browsing/content/common/file_type_policies.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "third_party/zlib/google/zip_reader.h"
namespace safe_browsing {
namespace {
class WriterDelegate : public zip::FileWriterDelegate {
public:
explicit WriterDelegate(base::File* file)
: zip::FileWriterDelegate(file), has_disk_error_(false) {}
WriterDelegate(const WriterDelegate&) = delete;
WriterDelegate& operator=(const WriterDelegate&) = delete;
~WriterDelegate() override = default;
bool PrepareOutput() override {
bool success = zip::FileWriterDelegate::PrepareOutput();
has_disk_error_ |= !success;
return success;
}
bool WriteBytes(const char* data, int num_bytes) override {
bool success = zip::FileWriterDelegate::WriteBytes(data, num_bytes);
has_disk_error_ |= !success;
return success;
}
bool has_disk_error() const { return has_disk_error_; }
private:
bool has_disk_error_;
};
} // namespace
ZipAnalyzer::ZipAnalyzer() = default;
ZipAnalyzer::~ZipAnalyzer() = default;
void ZipAnalyzer::Init() {
GetTempFile(
base::BindOnce(&ZipAnalyzer::OnGetTempFile, weak_factory_.GetWeakPtr()));
}
bool ZipAnalyzer::ResumeExtraction() {
while (const zip::ZipReader::Entry* const entry = reader_.Next()) {
// Clear the `temp_file` between extractions.
if (temp_file_.Seek(base::File::Whence::FROM_BEGIN, 0) != 0) {
PLOG(WARNING) << "Failed seek";
}
// Since this code is expected to run within a utility process, this call
// will fail on some platforms. We handle this by passing the length
// into `UpdateResultsForEntry`, which will only consider
// the appropriate bytes. See crbug.com/1309879 and crbug.com/774762.
if (!temp_file_.SetLength(0)) {
PLOG(WARNING) << "Failed truncate";
}
WriterDelegate writer(&temp_file_);
bool extract_success = reader_.ExtractCurrentEntry(
&writer, std::numeric_limits<uint64_t>::max());
has_encrypted_ |= entry->is_encrypted;
has_aes_encrypted_ |= entry->uses_aes_encryption;
has_disk_error_ |= writer.has_disk_error();
if (!extract_success && entry->is_encrypted) {
results()->encryption_info.password_status =
EncryptionInfo::kKnownIncorrect;
}
if (!UpdateResultsForEntry(temp_file_.Duplicate(),
GetRootPath().Append(entry->path),
writer.file_length(), entry->is_encrypted,
entry->is_directory, extract_success)) {
return false;
}
}
results()->encryption_info.is_encrypted = has_encrypted_;
if (has_encrypted_) {
base::UmaHistogramBoolean("SBClientDownload.EncryptedZipUsesAes",
has_aes_encrypted_);
if (has_aes_encrypted_ && password() && !password()->empty()) {
results()->encryption_info.password_status = EncryptionInfo::kUnknown;
} else if (results()->encryption_info.password_status !=
EncryptionInfo::kKnownIncorrect) {
results()->encryption_info.password_status =
EncryptionInfo::kKnownCorrect;
}
}
if (has_disk_error_) {
results()->analysis_result = ArchiveAnalysisResult::kDiskError;
} else if (reader_.ok()) {
results()->analysis_result = ArchiveAnalysisResult::kValid;
} else {
results()->analysis_result = ArchiveAnalysisResult::kFailedDuringIteration;
}
results()->success = reader_.ok() && !has_disk_error_;
return true;
}
base::WeakPtr<ArchiveAnalyzer> ZipAnalyzer::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void ZipAnalyzer::OnGetTempFile(base::File temp_file) {
if (!temp_file.IsValid()) {
InitComplete(ArchiveAnalysisResult::kFailedToOpenTempFile);
return;
}
if (!reader_.OpenFromPlatformFile(GetArchiveFile().GetPlatformFile())) {
InitComplete(ArchiveAnalysisResult::kUnknown);
return;
}
bool too_big_to_unpack =
base::checked_cast<uint64_t>(GetArchiveFile().GetLength()) >
FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze("zip");
if (too_big_to_unpack) {
InitComplete(ArchiveAnalysisResult::kTooLarge);
return;
}
temp_file_ = std::move(temp_file);
if (password().has_value()) {
reader_.SetPassword(*password());
}
InitComplete(ArchiveAnalysisResult::kValid);
}
} // namespace safe_browsing