Commit 34eedbc8 authored by Varun Khaneja's avatar Varun Khaneja Committed by Commit Bot

Use unrar to extract some metadata about files the .rar file.

- Uses the the Archive API to iterative over the files
  contained inside the downloaded .rar file.
- Identify if the .rar file contains any executables.
- Identify if the .rar file contains any archives.

This information isn't currently used to populate the CSBRR ping
sent to Safe Browsing. That's work for a follow-up CL.

All this is behind the flag: InspectDownloadedRarFiles

Note: This CL sends a fd/handle to the unrar library since code
running inside the sandbox can't open/create files.

Change-Id: Ie5d30040f51f59854d4a041652d171d704f4df78
Bug: 750327
Reviewed-on: https://chromium-review.googlesource.com/947664
Commit-Queue: Varun Khaneja <vakh@chromium.org>
Reviewed-by: default avatarNathan Parker <nparker@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Reviewed-by: default avatarLuke Z <lpz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#563016}
parent 024402fa
......@@ -13,16 +13,74 @@ proto_library("proto") {
]
}
source_set("safe_browsing") {
source_set("file_type_policies") {
sources = [
"file_type_policies.cc",
"file_type_policies.h",
]
if (safe_browsing_mode == 1) {
sources += [
deps = [
":proto",
"//base",
"//chrome/browser:resources",
"//ui/base",
]
}
if (safe_browsing_mode == 1) {
source_set("archive_analyzer_results") {
sources = [
"archive_analyzer_results.cc",
"archive_analyzer_results.h",
]
deps = [
"//base",
]
public_deps = [
"//components/safe_browsing:csd_proto",
]
}
source_set("rar_analyzer") {
sources = [
"rar_analyzer.cc",
"rar_analyzer.h",
]
deps = [
":archive_analyzer_results",
":file_type_policies",
"//base",
"//third_party/unrar:unrar",
]
defines = [
"_FILE_OFFSET_BITS=64",
"LARGEFILE_SOURCE",
"RAR_SMP",
"SILENT",
# The following is set to disable certain macro definitions in the unrar
# source code.
"CHROMIUM_UNRAR",
# Disables exceptions in unrar, replaces them with process termination.
"UNRAR_NO_EXCEPTIONS",
]
public_deps = [
"//components/safe_browsing:csd_proto",
]
}
}
source_set("safe_browsing") {
deps = [
":file_type_policies",
]
if (safe_browsing_mode == 1) {
sources = [
"binary_feature_extractor.cc",
"binary_feature_extractor.h",
"binary_feature_extractor_mac.cc",
......@@ -44,17 +102,20 @@ source_set("safe_browsing") {
if (is_posix) {
sources += [ "binary_feature_extractor_posix.cc" ]
}
}
public_deps = [
"//base:i18n",
"//chrome/browser:resources",
"//chrome/common:mojo_bindings",
"//chrome/common/safe_browsing:proto",
"//components/safe_browsing:csd_proto",
"//crypto",
"//ipc",
"//third_party/zlib/google:zip",
"//ui/base",
]
deps += [
":archive_analyzer_results",
":rar_analyzer",
]
public_deps = [
":proto",
"//base:i18n",
"//chrome/common:mojo_bindings",
"//components/safe_browsing:csd_proto",
"//crypto",
"//ipc",
"//third_party/zlib/google:zip",
]
}
}
include_rules = [
"+third_party/protobuf",
"+third_party/unrar",
"+third_party/zlib",
]
// 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 "build/build_config.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "chrome/common/safe_browsing/file_type_policies.h"
#include "third_party/unrar/src/unrar_wrapper.h"
namespace safe_browsing {
namespace rar_analyzer {
void AnalyzeRarFile(base::File rar_file,
const base::FilePath& rar_file_path,
ArchiveAnalyzerResults* results) {
auto archive = std::make_unique<third_party_unrar::Archive>();
archive->SetFileHandle(rar_file.GetPlatformFile());
std::wstring wide_rar_file_path(rar_file_path.value().begin(),
rar_file_path.value().end());
if (!archive->Open(wide_rar_file_path.c_str())) {
results->success = false;
// TODO(vakh): Log UMA here.
VLOG(1) << __FUNCTION__
<< ": Unable to open rar_file: " << rar_file.GetPlatformFile();
return;
}
if (!archive->IsArchive(/*EnableBroken=*/true)) {
// TODO(vakh): Log UMA here.
results->success = false;
VLOG(1) << __FUNCTION__
<< ": !IsArchive: rar_file: " << rar_file.GetPlatformFile();
return;
}
// TODO(vakh): Log UMA here.
results->success = true;
std::set<base::FilePath> archived_archive_filenames;
for (archive->ViewComment();
archive->ReadHeader() > 0 &&
archive->GetHeaderType() != third_party_unrar::kUnrarEndarcHead;
archive->SeekToNext()) {
VLOG(2) << __FUNCTION__ << ": FileName: " << archive->FileHead.FileName;
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
VLOG(2) << __FUNCTION__ << ": file_path: " << file_path.value().c_str();
bool is_executable =
FileTypePolicies::GetInstance()->IsCheckedBinaryFile(file_path);
VLOG(2) << __FUNCTION__ << ": is_executable: " << is_executable;
results->has_executable |= is_executable;
bool is_archive = FileTypePolicies::GetInstance()->IsArchiveFile(file_path);
VLOG(2) << __FUNCTION__ << ": is_archive: " << is_archive;
results->has_archive |= is_archive;
if (is_archive) {
archived_archive_filenames.insert(file_path.BaseName());
}
results->archived_archive_filenames.assign(
archived_archive_filenames.begin(), archived_archive_filenames.end());
}
}
} // namespace rar_analyzer
} // namespace safe_browsing
// 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.
//
// This file contains the rar file analysis implementation for download
// protection, which runs in a sandbox. The reason for running in a sandbox is
// to isolate the browser and other renderer processes from any vulnerabilities
// that the attacker-controlled download file may try to exploit.
//
// Here's the call flow for inspecting .rar files upon download:
// 1. File is downloaded.
// 2. |CheckClientDownloadRequest::AnalyzeFile()| is called to analyze the Safe
// Browsing reputation of the downloaded file.
// 3. It calls |CheckClientDownloadRequest::StartExtractRarFeatures()|, which
// creates an instance of |SandboxedRarAnalyzer|, and calls |Start()|.
// 4. |SandboxedRarAnalyzer::Start()| leads to a mojo call to
// |SafeArchiveAnalyzer::AnalyzeRarFile()| in a sandbox.
// 5. Finally, |SafeArchiveAnalyzer::AnalyzeRarFile()| calls |AnalyzeRarFile()|
// defined in this file to actually inspect the file.
#ifndef CHROME_COMMON_SAFE_BROWSING_RAR_ANALYZER_H_
#define CHROME_COMMON_SAFE_BROWSING_RAR_ANALYZER_H_
#include "base/files/file.h"
#include "base/files/file_path.h"
namespace safe_browsing {
struct ArchiveAnalyzerResults;
namespace rar_analyzer {
// |rar_file| is a platform-agnostic handle to the file. Since |AnalyzeRarFile|
// runs inside a sandbox, it isn't allowed to open file handles. So the file is
// opened in |SandboxedRarAnalyzer|, which runs in the browser process, and the
// handle is passed here. |rar_file_path| is the path to the same file. It is
// required only because the unrar library expects it to be used with a
// filename. The function populates the various fields in |results| based on the
// results of parsing the rar file.
// If the parsing fails for any reason, including crashing the sandbox process,
// the browser process considers the file safe.
void AnalyzeRarFile(base::File rar_file,
const base::FilePath& rar_file_path,
ArchiveAnalyzerResults* results);
} // namespace rar_analyzer
} // namespace safe_browsing
#endif // CHROME_COMMON_SAFE_BROWSING_RAR_ANALYZER_H_
......@@ -26,11 +26,6 @@ source_set("cpp") {
]
}
#TODO(crbug/750327): This dependency is here temporarily.
deps = [
"//third_party/unrar:unrar",
]
public_deps += [ "//chrome/common/safe_browsing" ]
}
}
......
......@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/process/process_handle.h"
#include "base/strings/stringprintf.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
......@@ -17,10 +18,10 @@
#include "services/service_manager/public/cpp/connector.h"
SandboxedRarAnalyzer::SandboxedRarAnalyzer(
const base::FilePath& rar_file,
const base::FilePath& rar_file_path,
const ResultCallback& callback,
service_manager::Connector* connector)
: file_path_(rar_file), callback_(callback), connector_(connector) {
: file_path_(rar_file_path), callback_(callback), connector_(connector) {
DCHECK(callback);
DCHECK(!file_path_.value().empty());
}
......@@ -37,7 +38,7 @@ void SandboxedRarAnalyzer::Start() {
SandboxedRarAnalyzer::~SandboxedRarAnalyzer() = default;
void SandboxedRarAnalyzer::AnalyzeFile() {
void SandboxedRarAnalyzer::AnalyzeFile(base::File file) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!analyzer_ptr_);
DCHECK(!file_path_.value().empty());
......@@ -48,34 +49,41 @@ void SandboxedRarAnalyzer::AnalyzeFile() {
&SandboxedRarAnalyzer::AnalyzeFileDone, base::Unretained(this),
safe_browsing::ArchiveAnalyzerResults()));
analyzer_ptr_->AnalyzeRarFile(
file_path_, base::BindOnce(&SandboxedRarAnalyzer::AnalyzeFileDone, this));
std::move(file), file_path_,
base::BindOnce(&SandboxedRarAnalyzer::AnalyzeFileDone, this));
}
void SandboxedRarAnalyzer::AnalyzeFileDone(
const safe_browsing::ArchiveAnalyzerResults& results) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
analyzer_ptr_.reset();
callback_.Run(results);
}
void SandboxedRarAnalyzer::PrepareFileToAnalyze() {
base::File file(file_path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (file_path_.value().empty()) {
// TODO(vakh): Add UMA metrics here to check how often this happens.
DLOG(ERROR) << "file_path_ empty!";
ReportFileFailure();
return;
}
base::File file(file_path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
// TODO(vakh): Add UMA metrics here to check how often this happens.
DLOG(ERROR) << "Could not open file: " << file_path_.value();
ReportFileFailure();
return;
}
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&SandboxedRarAnalyzer::AnalyzeFile, this));
base::BindOnce(&SandboxedRarAnalyzer::AnalyzeFile, this,
std::move(file)));
}
void SandboxedRarAnalyzer::ReportFileFailure() {
DCHECK(!analyzer_ptr_);
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(callback_, safe_browsing::ArchiveAnalyzerResults()));
......
......@@ -28,7 +28,7 @@ class SandboxedRarAnalyzer
using ResultCallback = base::RepeatingCallback<void(
const safe_browsing::ArchiveAnalyzerResults&)>;
SandboxedRarAnalyzer(const base::FilePath& rar_file,
SandboxedRarAnalyzer(const base::FilePath& rar_file_path,
const ResultCallback& callback,
service_manager::Connector* connector);
......@@ -50,7 +50,7 @@ class SandboxedRarAnalyzer
void ReportFileFailure();
// Starts the utility process and sends it a file analyze request.
void AnalyzeFile();
void AnalyzeFile(base::File file);
// The response containing the file analyze results.
void AnalyzeFileDone(const safe_browsing::ArchiveAnalyzerResults& results);
......
......@@ -26,7 +26,8 @@ interface SafeArchiveAnalyzer {
// Build flag FULL_SAFE_BROWSING: Analyze the |rar_file| for malicious
// download protection.
AnalyzeRarFile(mojo_base.mojom.FilePath rar_file)
AnalyzeRarFile(mojo_base.mojom.File rar_file,
mojo_base.mojom.FilePath rar_file_path)
=> (SafeArchiveAnalyzerResults results);
};
......
......@@ -7,6 +7,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "chrome/common/safe_browsing/rar_analyzer.h"
#include "chrome/common/safe_browsing/zip_analyzer.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
......@@ -44,15 +45,14 @@ void SafeArchiveAnalyzer::AnalyzeDmgFile(base::File dmg_file,
#endif
}
void SafeArchiveAnalyzer::AnalyzeRarFile(const base::FilePath& rar_file_path,
void SafeArchiveAnalyzer::AnalyzeRarFile(base::File rar_file,
const base::FilePath& rar_file_path,
AnalyzeRarFileCallback callback) {
DCHECK(rar_file.IsValid());
DCHECK(!rar_file_path.value().empty());
safe_browsing::ArchiveAnalyzerResults results;
base::File file(rar_file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
results.success = false;
}
// TODO(crbug/750327): Inspect |file|.
safe_browsing::rar_analyzer::AnalyzeRarFile(std::move(rar_file),
rar_file_path, &results);
std::move(callback).Run(results);
}
......@@ -26,7 +26,8 @@ class SafeArchiveAnalyzer : public chrome::mojom::SafeArchiveAnalyzer {
AnalyzeZipFileCallback callback) override;
void AnalyzeDmgFile(base::File dmg_file,
AnalyzeDmgFileCallback callback) override;
void AnalyzeRarFile(const base::FilePath& rar_file_path,
void AnalyzeRarFile(base::File rar_file,
const base::FilePath& rar_file_path,
AnalyzeRarFileCallback callback) override;
const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
......
......@@ -15,7 +15,8 @@ source_set("mac") {
deps = [
":dmg_common",
"//base",
"//chrome/common",
"//chrome/common/safe_browsing:archive_analyzer_results",
"//chrome/common/safe_browsing:safe_browsing",
"//components/safe_browsing:csd_proto",
]
}
......
......@@ -27,7 +27,6 @@ if (safe_browsing_mode == 1) {
"src/global.cpp",
"src/hash.cpp",
"src/headers.cpp",
"src/isnt.cpp",
"src/list.cpp",
"src/match.cpp",
"src/options.cpp",
......@@ -56,6 +55,9 @@ if (safe_browsing_mode == 1) {
"src/unpack.cpp",
"src/volume.cpp",
]
if (is_win) {
sources += [ "src/isnt.cpp" ]
}
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ "//build/config/compiler:no_chromium_code" ]
......@@ -64,6 +66,7 @@ if (safe_browsing_mode == 1) {
"_FILE_OFFSET_BITS=64",
"LARGEFILE_SOURCE",
"RAR_SMP",
"SILENT",
# The following is set to disable certain macro definitions in the unrar
# source code.
......
......@@ -24,4 +24,6 @@ Notable changes from upstream:
- Explicit use of parentheses to fix linter errors.
- Replace exceptions with terminating the current process. Guarded with the
macro UNRAR_NO_EXCEPTIONS.
- Pass a file handle to the rar file, instead of trying to open the rar file
inside the unrar library code. This is done because the unrar library code
operates inside a sandbox, so it doesn't have the permissions to open files.
......@@ -51,6 +51,11 @@ bool File::Open(const wchar *Name,uint Mode)
bool UpdateMode=(Mode & FMF_UPDATE)!=0;
bool WriteMode=(Mode & FMF_WRITE)!=0;
#ifdef _WIN_ALL
#if defined(CHROMIUM_UNRAR)
// Do not open a file handle since the sandbox doesn't allow it. Use the
// handle provided by the caller.
hNewFile = hOpenFile;
#else
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
if (UpdateMode)
Access|=GENERIC_WRITE;
......@@ -89,6 +94,14 @@ bool File::Open(const wchar *Name,uint Mode)
if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
ErrorType=FILE_NOTFOUND;
#endif // defined(CHROMIUM_UNRAR)
#else
#if defined(CHROMIUM_UNRAR)
// Do not open a file handle since the sandbox doesn't allow it. Use the
// handle provided by the caller.
int handle = hOpenFile;
#else
int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
#ifdef O_BINARY
......@@ -99,8 +112,9 @@ bool File::Open(const wchar *Name,uint Mode)
#endif
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
int handle=open(NameA,flags);
#endif // defined(CHROMIUM_UNRAR)
#ifdef LOCK_EX
#ifdef _OSF_SOURCE
......@@ -230,6 +244,8 @@ bool File::Close()
{
if (!SkipClose)
{
#if !defined(CHROMIUM_UNRAR)
// unrar should not close the file handle since it wasn't opened by unrar.
#ifdef _WIN_ALL
// We use the standard system handle for stdout in Windows
// and it must not be closed here.
......@@ -242,6 +258,7 @@ bool File::Close()
Success=fclose(hFile)!=EOF;
#endif
#endif
#endif // defined(CHROMIUM_UNRAR)
}
hFile=FILE_BAD_HANDLE;
}
......@@ -735,3 +752,9 @@ int64 File::Copy(File &Dest,int64 Length)
return CopySize;
}
#endif
#if defined(CHROMIUM_UNRAR)
void File::SetFileHandle(FileHandle hF) {
hOpenFile = hF;
}
#endif // defined(CHROMIUM_UNRAR)
......@@ -68,6 +68,10 @@ class File
wchar FileName[NM];
FILE_ERRORTYPE ErrorType;
#if defined(CHROMIUM_UNRAR)
FileHandle hOpenFile;
#endif // defined(CHROMIUM_UNRAR)
public:
File();
virtual ~File();
......@@ -111,6 +115,14 @@ class File
#ifdef _WIN_ALL
void RemoveSequentialFlag() {NoSequentialRead=true;}
#endif
#if defined(CHROMIUM_UNRAR)
// Since unrar runs in a sandbox, it doesn't have the permission to open
// files on the filesystem. Instead, the caller opens the file and passes
// the file handle to unrar. This handle is then used to read the file.
void SetFileHandle(FileHandle file);
#endif // defined(CHROMIUM_UNRAR)
#ifdef _UNIX
int GetFD()
{
......
#include "isnt.hpp"
#include "rar.hpp"
#ifdef _WIN_ALL
#include "versionhelpers.h"
DWORD WinNT()
{
if (!IsWinXpOrGreater()) return WNT_NONE;
if (!IsWinVistaOrGreater()) return WNT_WXP;
if (!IsWindowsXPOrGreater())
return WNT_NONE;
if (!IsWindowsVistaOrGreater())
return WNT_WXP;
if (!IsWindows7OrGreater()) return WNT_VISTA;
if (!IsWindows8OrGreater()) return WNT_W7;
if (!IsWindows8Point1OrGreater()) return WNT_W8;
......
#ifndef _RAR_ISNT_
#define _RAR_ISNT_
#ifdef _WIN_ALL
#include "windows.h"
enum WINNT_VERSION {
WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
......@@ -12,5 +12,3 @@ enum WINNT_VERSION {
DWORD WinNT();
#endif
#endif
// 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.
#ifndef THIRD_PARTY_UNRAR_SRC_UNRAR_WRAPPER_H_
#define THIRD_PARTY_UNRAR_SRC_UNRAR_WRAPPER_H_
#include "rar.hpp"
namespace third_party_unrar {
using ::Archive;
static const int kUnrarEndarcHead = ::HEAD_ENDARC;
} // namespace third_party_unrar
#endif // THIRD_PARTY_UNRAR_SRC_UNRAR_WRAPPER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment