blob: 1d35233895a5bcf33460e50bc623fe51fca2ff59 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/safe_browsing/rar_analyzer.h"
#include <memory>
#include <string>
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/i18n/streaming_utf8_validator.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "chrome/common/safe_browsing/download_type_util.h"
#include "chrome/common/safe_browsing/file_type_policies.h"
#include "components/safe_browsing/features.h"
#include "third_party/unrar/src/unrar_wrapper.h"
namespace safe_browsing {
namespace rar_analyzer {
void AnalyzeRarFile(base::File rar_file,
base::File temp_file,
ArchiveAnalyzerResults* results) {
results->success = false;
auto archive = std::make_unique<third_party_unrar::Archive>();
archive->SetFileHandle(rar_file.GetPlatformFile());
if (base::FeatureList::IsEnabled(kInspectRarContentFeature)) {
archive->SetTempFileHandle(temp_file.GetPlatformFile());
}
bool open_success = archive->Open(L"dummy.rar");
UMA_HISTOGRAM_BOOLEAN("SBClientDownload.RarOpenSuccess", open_success);
if (!open_success) {
DLOG(ERROR) << __FUNCTION__
<< ": Unable to open rar_file: " << rar_file.GetPlatformFile();
return;
}
bool is_valid_archive = archive->IsArchive(/*EnableBroken=*/true);
UMA_HISTOGRAM_BOOLEAN("SBClientDownload.RarValidArchive", is_valid_archive);
if (!is_valid_archive) {
DLOG(ERROR) << __FUNCTION__
<< ": !IsArchive: rar_file: " << rar_file.GetPlatformFile();
return;
}
if (base::FeatureList::IsEnabled(kInspectRarContentFeature)) {
auto command = std::make_unique<third_party_unrar::CommandData>();
command->ParseArg(const_cast<wchar_t*>(L"-p"));
command->ParseArg(const_cast<wchar_t*>(L"x"));
command->ParseDone();
third_party_unrar::CmdExtract extractor(command.get());
extractor.ExtractArchiveInit(*archive);
bool failed = false, repeat = true;
while (!failed || repeat) {
// Clear the |temp_file| between extractions.
temp_file.Seek(base::File::Whence::FROM_BEGIN, 0);
temp_file.SetLength(0);
size_t header_size = archive->ReadHeader();
repeat = false;
failed = !extractor.ExtractCurrentFile(
*archive, header_size, repeat); // |repeat| is passed by reference
if (archive->GetHeaderType() == third_party_unrar::kUnrarFileHead) {
std::wstring wide_filename(archive->FileHead.FileName);
#if defined(OS_WIN)
base::FilePath file_path(wide_filename);
#else
std::string filename(wide_filename.begin(), wide_filename.end());
base::FilePath file_path(filename);
#endif // OS_WIN
UpdateArchiveAnalyzerResultsWithFile(
file_path, &temp_file, archive->FileHead.Encrypted, results);
}
}
} else {
std::set<base::FilePath> archived_archive_filenames;
for (archive->ViewComment();
archive->ReadHeader() > 0 &&
archive->GetHeaderType() != third_party_unrar::kUnrarEndarcHead;
archive->SeekToNext()) {
std::wstring wide_filename(archive->FileHead.FileName);
#if defined(OS_WIN)
base::FilePath file_path(wide_filename);
#else
std::string filename(wide_filename.begin(), wide_filename.end());
base::FilePath file_path(filename);
#endif // OS_WIN
bool is_executable =
FileTypePolicies::GetInstance()->IsCheckedBinaryFile(file_path);
bool is_archive =
FileTypePolicies::GetInstance()->IsArchiveFile(file_path);
int64 unpacked_size =
archive->FileHead.UnpSize; // Read from header, may not be accurate.
// TODO(vakh): Log UMA if |unpacked_size| < 0.
base::FilePath basename = file_path.BaseName();
std::string basename_utf8(basename.AsUTF8Unsafe());
bool is_utf8_valid_basename =
base::StreamingUtf8Validator::Validate(basename_utf8);
if (is_archive) {
results->has_archive = true;
archived_archive_filenames.insert(basename);
ClientDownloadRequest::ArchivedBinary* archived_archive =
results->archived_binary.Add();
if (is_utf8_valid_basename)
archived_archive->set_file_basename(basename_utf8);
archived_archive->set_download_type(ClientDownloadRequest::ARCHIVE);
archived_archive->set_length(unpacked_size);
} else if (is_executable) {
results->has_executable = true;
ClientDownloadRequest::ArchivedBinary* archived_binary =
results->archived_binary.Add();
if (is_utf8_valid_basename)
archived_binary->set_file_basename(basename_utf8);
archived_binary->set_download_type(
download_type_util::GetDownloadType(file_path));
archived_binary->set_length(unpacked_size);
}
results->archived_archive_filenames.assign(
archived_archive_filenames.begin(), archived_archive_filenames.end());
}
}
results->success = true;
}
} // namespace rar_analyzer
} // namespace safe_browsing