Commit bacb343d authored by Penny MacNeil's avatar Penny MacNeil Committed by Commit Bot

[chrome_elf, third-party block support] Change log format.

- Log format changed to pass values instead of hashes.
- Log.path will now be passed untouched, preserving original case from
  section path.  This allows for potentially case-sensitive operations
  later.
- Testing added to ensure the expected case behaviour,
  ThirdPartyTest.PathCaseSensistive.
- Also, reordered arguments for GetFingerprintString, to be more
  intuitively tied to the format string.

R: pmonette@chromium.org
Test: chrome_elf_unittests.exe, ThirdParty*
Bug: 769590
Change-Id: Ib28ec13da7bf27168513031f583bd47e47da0dcc
Reviewed-on: https://chromium-review.googlesource.com/1136660Reviewed-by: default avatarPatrick Monette <pmonette@chromium.org>
Commit-Queue: Penny MacNeil <pennymac@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575033}
parent 71ac0427
...@@ -10,11 +10,15 @@ ...@@ -10,11 +10,15 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/sha1.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_runner_util.h" #include "base/task_runner_util.h"
#include "base/task_scheduler/post_task.h" #include "base/task_scheduler/post_task.h"
#include "chrome/browser/conflicts/module_blacklist_cache_util_win.h" #include "chrome/browser/conflicts/module_blacklist_cache_util_win.h"
#include "chrome_elf/third_party_dlls/logging_api.h" #include "chrome_elf/third_party_dlls/logging_api.h"
#include "chrome_elf/third_party_dlls/packed_list_format.h"
namespace { namespace {
...@@ -45,14 +49,31 @@ std::vector<third_party_dlls::PackedListModule> DrainLogOnBackgroundTask() { ...@@ -45,14 +49,31 @@ std::vector<third_party_dlls::PackedListModule> DrainLogOnBackgroundTask() {
// get better visibility into all modules that loads into the browser // get better visibility into all modules that loads into the browser
// process. // process.
if (entry->type == third_party_dlls::LogType::kBlocked) { if (entry->type == third_party_dlls::LogType::kBlocked) {
// No log path should be empty.
DCHECK(entry->path_len);
blocked_modules.emplace_back(); blocked_modules.emplace_back();
third_party_dlls::PackedListModule& module = blocked_modules.back(); third_party_dlls::PackedListModule& module = blocked_modules.back();
std::copy(std::begin(entry->basename_hash), // Fill in a PackedListModule from the log entry.
std::end(entry->basename_hash), std::string hash_string =
std::begin(module.basename_hash)); base::SHA1HashString(third_party_dlls::GetFingerprintString(
std::copy(std::begin(entry->code_id_hash), std::end(entry->code_id_hash), entry->time_date_stamp, entry->module_size));
std::copy(std::begin(hash_string), std::end(hash_string),
std::begin(module.code_id_hash)); std::begin(module.code_id_hash));
// |entry->path| is a UTF-8 device path. A hash of the
// lowercase, UTF-8 basename is needed for |module.basename_hash|.
base::FilePath file_path(base::UTF8ToUTF16(entry->path));
std::wstring basename = base::i18n::ToLower(file_path.BaseName().value());
hash_string = base::UTF16ToUTF8(basename);
hash_string = base::SHA1HashString(hash_string);
std::copy(std::begin(hash_string), std::end(hash_string),
std::begin(module.basename_hash));
module.time_date_stamp = now_time_date_stamp; module.time_date_stamp = now_time_date_stamp;
// TODO(pmonette): |file_path| is ready for
// base::DevicePathToDriveLetterPath() here if needed.
// Consider making these path conversions more efficient
// by caching the local mounted devices and corresponding
// drive paths once.
} }
tracker += third_party_dlls::GetLogEntrySize(entry->path_len); tracker += third_party_dlls::GetLogEntrySize(entry->path_len);
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "base/win/windows_types.h" #include "base/win/windows_types.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome_elf/chrome_elf_main.h" #include "chrome_elf/chrome_elf_main.h"
#include "chrome_elf/sha1/sha1.h"
#include "chrome_elf/third_party_dlls/logging_api.h" #include "chrome_elf/third_party_dlls/logging_api.h"
// This function is a temporary workaround for https://crbug.com/655788. We // This function is a temporary workaround for https://crbug.com/655788. We
...@@ -48,8 +47,8 @@ void SetMetricsClientId(const char* client_id) {} ...@@ -48,8 +47,8 @@ void SetMetricsClientId(const char* client_id) {}
struct TestLogEntry { struct TestLogEntry {
third_party_dlls::LogType log_type; third_party_dlls::LogType log_type;
uint8_t basename_hash[elf_sha1::kSHA1Length]; uint32_t module_size;
uint8_t code_id_hash[elf_sha1::kSHA1Length]; uint32_t time_date_stamp;
}; };
// This test stub always writes 2 hardcoded entries into the buffer, if the // This test stub always writes 2 hardcoded entries into the buffer, if the
...@@ -59,20 +58,8 @@ uint32_t DrainLog(uint8_t* buffer, ...@@ -59,20 +58,8 @@ uint32_t DrainLog(uint8_t* buffer,
uint32_t* log_remaining) { uint32_t* log_remaining) {
// Alternate between log types. // Alternate between log types.
TestLogEntry kTestLogEntries[] = { TestLogEntry kTestLogEntries[] = {
{ {third_party_dlls::LogType::kAllowed, 0x9901, 0x12345678},
third_party_dlls::LogType::kAllowed, {third_party_dlls::LogType::kBlocked, 0x9902, 0x12345678},
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19},
{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49},
},
{
third_party_dlls::LogType::kBlocked,
{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB},
{0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD},
},
}; };
// Each entry shares the module path for convenience. // Each entry shares the module path for convenience.
...@@ -94,10 +81,8 @@ uint32_t DrainLog(uint8_t* buffer, ...@@ -94,10 +81,8 @@ uint32_t DrainLog(uint8_t* buffer,
reinterpret_cast<third_party_dlls::LogEntry*>(tracker); reinterpret_cast<third_party_dlls::LogEntry*>(tracker);
log_entry->type = test_entry.log_type; log_entry->type = test_entry.log_type;
::memcpy(log_entry->basename_hash, test_entry.basename_hash, log_entry->module_size = test_entry.module_size;
sizeof(test_entry.basename_hash)); log_entry->time_date_stamp = test_entry.time_date_stamp;
::memcpy(log_entry->code_id_hash, test_entry.code_id_hash,
sizeof(test_entry.code_id_hash));
log_entry->path_len = kModulePathLength; log_entry->path_len = kModulePathLength;
::memcpy(log_entry->path, kModulePath, log_entry->path_len + 1); ::memcpy(log_entry->path, kModulePath, log_entry->path_len + 1);
......
...@@ -148,7 +148,9 @@ bool UTF16ToUTF8(const std::wstring& utf16, std::string* utf8) { ...@@ -148,7 +148,9 @@ bool UTF16ToUTF8(const std::wstring& utf16, std::string* utf8) {
} }
// Helper function to contain the data mining for the values needed. // Helper function to contain the data mining for the values needed.
// - All strings returned are lowercase UTF-8. // - |image_name| and |section_basename| will be lowercased. |section_path|
// will be left untouched, preserved for case-sensitive operations with it.
// - All strings returned are UTF-8. Treat accordingly.
// - This function succeeds if image_name || section_* is found. // - This function succeeds if image_name || section_* is found.
// Note: |section_path| contains |section_basename|, if the section name is // Note: |section_path| contains |section_basename|, if the section name is
// successfully mined. // successfully mined.
...@@ -197,12 +199,11 @@ bool GetDataFromImage(PVOID buffer, ...@@ -197,12 +199,11 @@ bool GetDataFromImage(PVOID buffer,
*image_name = std::string(name, ::strnlen(name, MAX_PATH)); *image_name = std::string(name, ::strnlen(name, MAX_PATH));
} }
// Lowercase |image_name|.
for (size_t i = 0; i < image_name->size(); i++) for (size_t i = 0; i < image_name->size(); i++)
(*image_name)[i] = tolower((*image_name)[i]); (*image_name)[i] = tolower((*image_name)[i]);
std::wstring temp_section_path = GetSectionName(buffer); std::wstring temp_section_path = GetSectionName(buffer);
for (size_t i = 0; i < temp_section_path.size(); i++)
temp_section_path[i] = towlower(temp_section_path[i]);
// For now, consider it a success if at least one source results in a name. // For now, consider it a success if at least one source results in a name.
// Allow for the rare case of one or the other not being there. // Allow for the rare case of one or the other not being there.
...@@ -223,7 +224,11 @@ bool GetDataFromImage(PVOID buffer, ...@@ -223,7 +224,11 @@ bool GetDataFromImage(PVOID buffer,
temp_section_basename = temp_section_path.substr(sep + 1); temp_section_basename = temp_section_path.substr(sep + 1);
} }
// Convert strings from UTF-16 to UTF-8. // Lowercase |section_basename|.
for (size_t i = 0; i < temp_section_basename.size(); i++)
temp_section_basename[i] = towlower(temp_section_basename[i]);
// Convert section strings from UTF-16 to UTF-8.
return UTF16ToUTF8(temp_section_path, section_path) && return UTF16ToUTF8(temp_section_path, section_path) &&
UTF16ToUTF8(temp_section_basename, section_basename); UTF16ToUTF8(temp_section_basename, section_basename);
} }
...@@ -288,54 +293,45 @@ NTSTATUS NewNtMapViewOfSectionImpl( ...@@ -288,54 +293,45 @@ NTSTATUS NewNtMapViewOfSectionImpl(
if (!section_basename.empty()) if (!section_basename.empty())
section_basename_hash = elf_sha1::SHA1HashString(section_basename); section_basename_hash = elf_sha1::SHA1HashString(section_basename);
std::string fingerprint_hash = std::string fingerprint_hash =
GetFingerprintString(image_size, time_date_stamp); GetFingerprintString(time_date_stamp, image_size);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash); fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
// Check sources for blacklist decision. // Check sources for blacklist decision.
bool block = false; bool block = false;
std::string* name_matched = nullptr;
if (!image_name_hash.empty() && if (!image_name_hash.empty() &&
IsModuleListed(image_name_hash, fingerprint_hash)) { IsModuleListed(image_name_hash, fingerprint_hash)) {
// 1) Third-party DLL blacklist, check for image name from PE header. // 1) Third-party DLL blacklist, check for image name from PE header.
block = true; block = true;
name_matched = &image_name_hash;
} else if (!section_basename_hash.empty() && } else if (!section_basename_hash.empty() &&
section_basename_hash.compare(image_name_hash) != 0 && section_basename_hash.compare(image_name_hash) != 0 &&
IsModuleListed(section_basename_hash, fingerprint_hash)) { IsModuleListed(section_basename_hash, fingerprint_hash)) {
// 2) Third-party DLL blacklist, check for image name from the section. // 2) Third-party DLL blacklist, check for image name from the section.
block = true; block = true;
name_matched = &section_basename_hash;
} else if (!image_name.empty() && blacklist::DllMatch(image_name)) { } else if (!image_name.empty() && blacklist::DllMatch(image_name)) {
// 3) Hard-coded blacklist with name from PE header (deprecated). // 3) Hard-coded blacklist with name from PE header (deprecated).
block = true; block = true;
name_matched = &image_name_hash;
} else if (!section_basename.empty() && } else if (!section_basename.empty() &&
section_basename.compare(image_name) != 0 && section_basename.compare(image_name) != 0 &&
blacklist::DllMatch(section_basename)) { blacklist::DllMatch(section_basename)) {
// 4) Hard-coded blacklist with name from the section (deprecated). // 4) Hard-coded blacklist with name from the section (deprecated).
block = true; block = true;
name_matched = &section_basename_hash;
} else {
// No block.
// Ensure a non-null image name for the log. Prefer the section basename
// (to match the path).
name_matched =
section_basename.empty() ? &image_name_hash : &section_basename_hash;
} }
// Else, no block.
// UNMAP the view. This image is being blocked. // UNMAP the view. This image is being blocked.
if (block) { if (block) {
assert(name_matched);
assert(g_nt_unmap_view_of_section_func); assert(g_nt_unmap_view_of_section_func);
g_nt_unmap_view_of_section_func(process, *base); g_nt_unmap_view_of_section_func(process, *base);
ret = STATUS_UNSUCCESSFUL; ret = STATUS_UNSUCCESSFUL;
} }
// LOG! // LOG!
// - If there was a failure getting |section_path|, at least pass image_name.
LogLoadAttempt((block ? third_party_dlls::LogType::kBlocked LogLoadAttempt((block ? third_party_dlls::LogType::kBlocked
: third_party_dlls::LogType::kAllowed), : third_party_dlls::LogType::kAllowed),
*name_matched, fingerprint_hash, section_path); image_size, time_date_stamp,
section_path.empty() ? image_name : section_path);
return ret; return ret;
} }
......
...@@ -32,15 +32,16 @@ enum LogType : uint8_t { ...@@ -32,15 +32,16 @@ enum LogType : uint8_t {
// (Full path not required for a blacklisted load attempt log.) // (Full path not required for a blacklisted load attempt log.)
struct LogEntry { struct LogEntry {
LogType type; LogType type;
uint8_t basename_hash[20]; uint32_t module_size;
uint8_t code_id_hash[20]; uint32_t time_date_stamp;
// Number of characters in |path| string, not including null terminator. // Number of characters in |path| string, not including null terminator.
uint32_t path_len; uint32_t path_len;
// UTF-8 full module path, null termination guaranteed. // UTF-8 full module path, null termination guaranteed.
char path[1]; char path[1];
}; };
static_assert(sizeof(LogEntry) == 52, static_assert(sizeof(LogEntry) == 20,
"Ensure expectations for padding and alignment are correct. " "Ensure expectations for padding and alignment are correct. "
"If this changes, double check GetLogEntrySize() calculation."); "If this changes, double check GetLogEntrySize() calculation.");
......
...@@ -26,8 +26,8 @@ HANDLE g_notification_event = nullptr; ...@@ -26,8 +26,8 @@ HANDLE g_notification_event = nullptr;
// This structure will be translated into LogEntry when draining log. // This structure will be translated into LogEntry when draining log.
struct LogEntryInternal { struct LogEntryInternal {
uint8_t basename_hash[elf_sha1::kSHA1Length]; uint32_t image_size;
uint8_t code_id_hash[elf_sha1::kSHA1Length]; uint32_t time_date_stamp;
std::string full_path; std::string full_path;
}; };
...@@ -36,8 +36,8 @@ void TranslateEntry(LogType log_type, ...@@ -36,8 +36,8 @@ void TranslateEntry(LogType log_type,
const LogEntryInternal& src, const LogEntryInternal& src,
LogEntry* dst) { LogEntry* dst) {
dst->type = log_type; dst->type = log_type;
::memcpy(dst->basename_hash, src.basename_hash, elf_sha1::kSHA1Length); dst->module_size = src.image_size;
::memcpy(dst->code_id_hash, src.code_id_hash, elf_sha1::kSHA1Length); dst->time_date_stamp = src.time_date_stamp;
// Sanity check - there should be no LogEntryInternal with a too long path. // Sanity check - there should be no LogEntryInternal with a too long path.
// LogLoadAttempt() ensures this. // LogLoadAttempt() ensures this.
...@@ -71,8 +71,7 @@ class Log { ...@@ -71,8 +71,7 @@ class Log {
void AddEntry(LogEntryInternal&& entry) { void AddEntry(LogEntryInternal&& entry) {
// Sanity checks. If load blocked, do not add duplicate logs. // Sanity checks. If load blocked, do not add duplicate logs.
if (entries_.size() == kMaxLogEntries || if (entries_.size() == kMaxLogEntries ||
(log_type_ == LogType::kBlocked && (log_type_ == LogType::kBlocked && ContainsEntry(entry))) {
ContainsEntry(entry.basename_hash, entry.code_id_hash))) {
return; return;
} }
entries_.push_back(std::move(entry)); entries_.push_back(std::move(entry));
...@@ -114,11 +113,12 @@ class Log { ...@@ -114,11 +113,12 @@ class Log {
private: private:
// Logs are currently unordered, so just loop. // Logs are currently unordered, so just loop.
// - Returns true if the given hashes already exist in the log. // - Returns true if the given hashes already exist in the log.
bool ContainsEntry(const uint8_t* basename_hash, bool ContainsEntry(const LogEntryInternal& new_entry) const {
const uint8_t* code_id_hash) const {
for (auto entry : entries_) { for (auto entry : entries_) {
if (!elf_sha1::CompareHashes(basename_hash, entry.basename_hash) && // Compare strings last, only if everything else matches, for efficiency.
!elf_sha1::CompareHashes(code_id_hash, entry.code_id_hash)) { if (new_entry.image_size == entry.image_size &&
new_entry.time_date_stamp == entry.time_date_stamp &&
new_entry.full_path.compare(entry.full_path) == 0) {
return true; return true;
} }
} }
...@@ -162,22 +162,18 @@ Log& GetAllowedLog() { ...@@ -162,22 +162,18 @@ Log& GetAllowedLog() {
// This is called from inside a hook shim, so don't bother with return status. // This is called from inside a hook shim, so don't bother with return status.
void LogLoadAttempt(LogType log_type, void LogLoadAttempt(LogType log_type,
const std::string& basename_hash, uint32_t image_size,
const std::string& code_id_hash, uint32_t time_date_stamp,
const std::string& full_image_path) { const std::string& full_image_path) {
assert(g_log_mutex); assert(g_log_mutex);
assert(!basename_hash.empty() && !code_id_hash.empty());
assert(basename_hash.length() == elf_sha1::kSHA1Length &&
code_id_hash.length() == elf_sha1::kSHA1Length);
if (::WaitForSingleObject(g_log_mutex, kMaxMutexWaitMs) != WAIT_OBJECT_0) if (::WaitForSingleObject(g_log_mutex, kMaxMutexWaitMs) != WAIT_OBJECT_0)
return; return;
// Build the new log entry. // Build the new log entry.
LogEntryInternal entry; LogEntryInternal entry;
::memcpy(&entry.basename_hash[0], basename_hash.data(), entry.image_size = image_size;
elf_sha1::kSHA1Length); entry.time_date_stamp = time_date_stamp;
::memcpy(&entry.code_id_hash[0], code_id_hash.data(), elf_sha1::kSHA1Length);
entry.full_path = full_image_path; entry.full_path = full_image_path;
// Edge condition. Ensure the path length is <= max(uint32_t) - 1. // Edge condition. Ensure the path length is <= max(uint32_t) - 1.
......
...@@ -18,14 +18,13 @@ namespace third_party_dlls { ...@@ -18,14 +18,13 @@ namespace third_party_dlls {
// Adds a module load attempt to the internal load log. // Adds a module load attempt to the internal load log.
// - |log_type| indicates the type of logging. // - |log_type| indicates the type of logging.
// - |basename_hash| and |code_id_hash| must each point to a buffer of size // - |image_size| and |time_date_stamp| from the PE headers.
// elf_sha1::kSHA1Length, holding a SHA-1 digest (of the module's basename and // - |full_image_path| indicates the full path of the loaded image.
// code identifier, respectively). // - Note: if there was any failure retrieving the full path, pass at least the
// - For loads that are allowed, |full_image_path| indicates the full path of // basename for |full_image_path|.
// the loaded image.
void LogLoadAttempt(LogType log_type, void LogLoadAttempt(LogType log_type,
const std::string& basename_hash, uint32_t image_size,
const std::string& code_id_hash, uint32_t time_date_stamp,
const std::string& full_image_path); const std::string& full_image_path);
// Initialize internal logs. // Initialize internal logs.
......
...@@ -28,36 +28,14 @@ struct NotificationHandlerArguments { ...@@ -28,36 +28,14 @@ struct NotificationHandlerArguments {
}; };
struct TestEntry { struct TestEntry {
uint8_t basename_hash[elf_sha1::kSHA1Length]; uint32_t image_size;
uint8_t code_id_hash[elf_sha1::kSHA1Length]; uint32_t time_date_stamp;
}; };
// Sample TestEntries - hashes are the same, except for first bytes. // Sample TestEntries
TestEntry kTestLogs[] = { TestEntry kTestLogs[] = {
{{0x11, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20, {0x9901, 0x12345678}, {0x9902, 0x12345678}, {0x9903, 0x12345678},
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}, {0x9904, 0x12345678}, {0x9905, 0x12345678}, {0x9906, 0x12345678},
{0x22, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x33, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x44, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x55, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x66, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x77, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x88, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x99, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0xaa, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0xbb, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0xcc, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
}; };
// Be sure to test the padding/alignment issues well here. // Be sure to test the padding/alignment issues well here.
...@@ -82,12 +60,9 @@ void VerifyBuffer(uint8_t* buffer, uint32_t buffer_size) { ...@@ -82,12 +60,9 @@ void VerifyBuffer(uint8_t* buffer, uint32_t buffer_size) {
while (tracker < buffer + buffer_size) { while (tracker < buffer + buffer_size) {
entry = reinterpret_cast<LogEntry*>(tracker); entry = reinterpret_cast<LogEntry*>(tracker);
EXPECT_EQ(elf_sha1::CompareHashes(entry->basename_hash, EXPECT_EQ(entry->module_size, kTestLogs[index].image_size);
kTestLogs[index].basename_hash), EXPECT_EQ(entry->time_date_stamp, kTestLogs[index].time_date_stamp);
0);
EXPECT_EQ(elf_sha1::CompareHashes(entry->code_id_hash,
kTestLogs[index].code_id_hash),
0);
if (entry->path_len) if (entry->path_len)
EXPECT_STREQ(entry->path, kTestPaths[index].c_str()); EXPECT_STREQ(entry->path, kTestPaths[index].c_str());
...@@ -151,19 +126,13 @@ TEST(ThirdParty, Logs) { ...@@ -151,19 +126,13 @@ TEST(ThirdParty, Logs) {
ASSERT_EQ(InitLogs(), ThirdPartyStatus::kSuccess); ASSERT_EQ(InitLogs(), ThirdPartyStatus::kSuccess);
for (size_t i = 0; i < arraysize(kTestLogs); ++i) { for (size_t i = 0; i < arraysize(kTestLogs); ++i) {
std::string fingerprint_hash(
reinterpret_cast<char*>(kTestLogs[i].code_id_hash),
elf_sha1::kSHA1Length);
std::string name_hash(reinterpret_cast<char*>(kTestLogs[i].basename_hash),
elf_sha1::kSHA1Length);
// Add some blocked entries. // Add some blocked entries.
LogLoadAttempt(LogType::kBlocked, name_hash, fingerprint_hash, LogLoadAttempt(LogType::kBlocked, kTestLogs[i].image_size,
std::string()); kTestLogs[i].time_date_stamp, std::string());
// Add some allowed entries. // Add some allowed entries.
LogLoadAttempt(LogType::kAllowed, name_hash, fingerprint_hash, LogLoadAttempt(LogType::kAllowed, kTestLogs[i].image_size,
kTestPaths[i]); kTestLogs[i].time_date_stamp, kTestPaths[i]);
} }
uint32_t initial_log = 0; uint32_t initial_log = 0;
...@@ -206,15 +175,9 @@ TEST(ThirdParty, LogNotifications) { ...@@ -206,15 +175,9 @@ TEST(ThirdParty, LogNotifications) {
nullptr, 0, &NotificationHandler, &handler_data, 0, nullptr)); nullptr, 0, &NotificationHandler, &handler_data, 0, nullptr));
for (size_t i = 0; i < handler_data.logs_expected; ++i) { for (size_t i = 0; i < handler_data.logs_expected; ++i) {
std::string fingerprint_hash(
reinterpret_cast<char*>(kTestLogs[i].code_id_hash),
elf_sha1::kSHA1Length);
std::string name_hash(reinterpret_cast<char*>(kTestLogs[i].basename_hash),
elf_sha1::kSHA1Length);
// Add blocked entries - type doesn't matter in this test. // Add blocked entries - type doesn't matter in this test.
LogLoadAttempt(LogType::kBlocked, name_hash, fingerprint_hash, LogLoadAttempt(LogType::kBlocked, kTestLogs[i].image_size,
std::string()); kTestLogs[i].time_date_stamp, std::string());
} }
EXPECT_EQ(::WaitForSingleObject(thread.Get(), kWaitTimeoutMs * 2), EXPECT_EQ(::WaitForSingleObject(thread.Get(), kWaitTimeoutMs * 2),
......
...@@ -98,7 +98,7 @@ PackedListModule GeneratePackedListModule(const std::string& image_name, ...@@ -98,7 +98,7 @@ PackedListModule GeneratePackedListModule(const std::string& image_name,
assert(!image_name.empty()); assert(!image_name.empty());
// SHA1 hash the two strings, and copy them into the new struct. // SHA1 hash the two strings, and copy them into the new struct.
std::string code_id = GetFingerprintString(imagesize, timedatestamp); std::string code_id = GetFingerprintString(timedatestamp, imagesize);
code_id = elf_sha1::SHA1HashString(code_id); code_id = elf_sha1::SHA1HashString(code_id);
std::string name_hash = elf_sha1::SHA1HashString(image_name); std::string name_hash = elf_sha1::SHA1HashString(image_name);
...@@ -487,6 +487,42 @@ TEST_F(ThirdPartyTest, SHA1SanityCheck) { ...@@ -487,6 +487,42 @@ TEST_F(ThirdPartyTest, SHA1SanityCheck) {
0); 0);
} }
// Test that full section path is left alone, in terms of case.
TEST_F(ThirdPartyTest, PathCaseSensitive) {
// Rename module to have mixed case.
ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName2, GetScopedTempDirValue(),
kTestDllName1MixedCase));
// 1) Sanity check that the hook GetDataFromImage() mining leaves the
// section path alone.
TestModuleData module_data = {};
ASSERT_TRUE(GetTestModuleData(kTestDllName1MixedCase, GetScopedTempDirValue(),
&module_data));
// Reminder: this string is actually UTF-8, but this test ensures it is ascii.
// Also, |section_path| will be a device path, so convert to drive letter
// before comparing.
base::FilePath drive;
ASSERT_TRUE(base::DevicePathToDriveLetterPath(
base::FilePath(base::ASCIIToUTF16(module_data.section_path)), &drive));
EXPECT_EQ(drive.value().compare(
MakePath(GetScopedTempDirValue(), kTestDllName1MixedCase)),
0);
// 2) Now check an actual log. Successful DLL load with no blacklist is fine
// for this test.
base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
cmd_line1.AppendArgNative(GetBlTestFilePath());
cmd_line1.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line1.AppendArgNative(
MakePath(GetScopedTempDirValue(), kTestDllName1MixedCase));
int exit_code = 0;
LaunchChildAndWait(cmd_line1, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadSuccess, exit_code);
}
// Test the status-code passing in registry. // Test the status-code passing in registry.
TEST_F(ThirdPartyTest, StatusCodes) { TEST_F(ThirdPartyTest, StatusCodes) {
// 1. Enable reg override for test net. // 1. Enable reg override for test net.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <shellapi.h> #include <shellapi.h>
#include "base/files/file.h" #include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/scoped_native_library.h" #include "base/scoped_native_library.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/test_reg_util_win.h" #include "base/test/test_reg_util_win.h"
...@@ -47,6 +48,23 @@ void RegRedirect(registry_util::RegistryOverrideManager* rom) { ...@@ -47,6 +48,23 @@ void RegRedirect(registry_util::RegistryOverrideManager* rom) {
nt::SetTestingOverride(nt::HKCU, temp); nt::SetTestingOverride(nt::HKCU, temp);
} }
// Compare an argument path with a module-load log path.
// - |arg_path| is a UTF-16 drive path.
// - |log.section_path| is UTF-8, and will be a device path, so convert to drive
// letter before comparing.
bool MatchPath(const wchar_t* arg_path, const third_party_dlls::LogEntry& log) {
base::FilePath drive_path;
if (!base::DevicePathToDriveLetterPath(
base::FilePath(base::UTF8ToUTF16(log.path)), &drive_path)) {
return false;
}
if (drive_path.value().compare(arg_path) != 0)
return false;
return true;
}
} // namespace } // namespace
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
...@@ -110,7 +128,9 @@ int main() { ...@@ -110,7 +128,9 @@ int main() {
case kTestOnlyInitialization: case kTestOnlyInitialization:
break; break;
// Single DLL load. // Single DLL load.
case kTestSingleDllLoad: { case kTestSingleDllLoad:
// Single DLL load with log path scrutiny.
case kTestLogPath: {
if (argument_count < 4) if (argument_count < 4)
return kMissingArgument; return kMissingArgument;
const wchar_t* dll_name = argv[3]; const wchar_t* dll_name = argv[3];
...@@ -127,6 +147,9 @@ int main() { ...@@ -127,6 +147,9 @@ int main() {
bytes = DrainLog(&buffer[0], bytes, nullptr); bytes = DrainLog(&buffer[0], bytes, nullptr);
third_party_dlls::LogEntry* entry = third_party_dlls::LogEntry* entry =
reinterpret_cast<third_party_dlls::LogEntry*>(&buffer[0]); reinterpret_cast<third_party_dlls::LogEntry*>(&buffer[0]);
if (!bytes || bytes < third_party_dlls::GetLogEntrySize(entry->path_len))
return kBadLogEntrySize;
if ((code == kDllLoadFailed && if ((code == kDllLoadFailed &&
entry->type != third_party_dlls::kBlocked) || entry->type != third_party_dlls::kBlocked) ||
(code == kDllLoadSuccess && (code == kDllLoadSuccess &&
...@@ -134,6 +157,9 @@ int main() { ...@@ -134,6 +157,9 @@ int main() {
return kUnexpectedLog; return kUnexpectedLog;
} }
if (test_id == kTestLogPath && !MatchPath(dll_name, *entry))
return kUnexpectedSectionPath;
return code; return code;
} }
// Unsupported argument. // Unsupported argument.
......
...@@ -21,11 +21,14 @@ enum ExitCode { ...@@ -21,11 +21,14 @@ enum ExitCode {
kUnsupportedTestId = -7, kUnsupportedTestId = -7,
kEmptyLog = -8, kEmptyLog = -8,
kUnexpectedLog = -9, kUnexpectedLog = -9,
kUnexpectedSectionPath = -10,
kBadLogEntrySize = -11,
}; };
enum TestId { enum TestId {
kTestOnlyInitialization = 1, kTestOnlyInitialization = 1,
kTestSingleDllLoad = 2, kTestSingleDllLoad = 2,
kTestLogPath = 3,
}; };
} // namespace main_unittest_exe } // namespace main_unittest_exe
......
...@@ -204,14 +204,14 @@ TEST_F(ThirdPartyFileTest, Success) { ...@@ -204,14 +204,14 @@ TEST_F(ThirdPartyFileTest, Success) {
// Test matching. // Test matching.
for (const auto& test_module : GetTestArray()) { for (const auto& test_module : GetTestArray()) {
fingerprint_hash = fingerprint_hash =
GetFingerprintString(test_module.imagesize, test_module.timedatestamp); GetFingerprintString(test_module.timedatestamp, test_module.imagesize);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash); fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
name_hash = elf_sha1::SHA1HashString(test_module.basename); name_hash = elf_sha1::SHA1HashString(test_module.basename);
EXPECT_TRUE(IsModuleListed(name_hash, fingerprint_hash)); EXPECT_TRUE(IsModuleListed(name_hash, fingerprint_hash));
} }
// Test a failure to match. // Test a failure to match.
fingerprint_hash = GetFingerprintString(1337, 0x12345678); fingerprint_hash = GetFingerprintString(0x12345678, 1337);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash); fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
name_hash = elf_sha1::SHA1HashString("booya.dll"); name_hash = elf_sha1::SHA1HashString("booya.dll");
EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash)); EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash));
...@@ -222,7 +222,7 @@ TEST_F(ThirdPartyFileTest, NoFiles) { ...@@ -222,7 +222,7 @@ TEST_F(ThirdPartyFileTest, NoFiles) {
// kFileNotFound is a non-fatal status code. // kFileNotFound is a non-fatal status code.
ASSERT_EQ(InitFromFile(), ThirdPartyStatus::kFileNotFound); ASSERT_EQ(InitFromFile(), ThirdPartyStatus::kFileNotFound);
std::string fingerprint_hash = GetFingerprintString(1337, 0x12345678); std::string fingerprint_hash = GetFingerprintString(0x12345678, 1337);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash); fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
std::string name_hash = elf_sha1::SHA1HashString("booya.dll"); std::string name_hash = elf_sha1::SHA1HashString("booya.dll");
EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash)); EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash));
......
...@@ -14,8 +14,8 @@ const wchar_t kThirdPartyRegKeyName[] = L"\\ThirdParty"; ...@@ -14,8 +14,8 @@ const wchar_t kThirdPartyRegKeyName[] = L"\\ThirdParty";
// Subkey value of type REG_SZ to hold a full path to a packed-list file. // Subkey value of type REG_SZ to hold a full path to a packed-list file.
const wchar_t kBlFilePathRegValue[] = L"BlFilePath"; const wchar_t kBlFilePathRegValue[] = L"BlFilePath";
std::string GetFingerprintString(uint32_t image_size, std::string GetFingerprintString(uint32_t time_data_stamp,
uint32_t time_data_stamp) { uint32_t image_size) {
// Max hex 32-bit value is 8 characters long. 2*8+1. // Max hex 32-bit value is 8 characters long. 2*8+1.
char buffer[17] = {}; char buffer[17] = {};
::snprintf(buffer, sizeof(buffer), "%08X%x", time_data_stamp, image_size); ::snprintf(buffer, sizeof(buffer), "%08X%x", time_data_stamp, image_size);
......
...@@ -68,7 +68,7 @@ struct PackedListModule { ...@@ -68,7 +68,7 @@ struct PackedListModule {
// and OptionalHeader.SizeOfImage with the formatting string %08X%x. // and OptionalHeader.SizeOfImage with the formatting string %08X%x.
// - To be used for PackedListModule.code_id_hash, IsModuleListed() and logging // - To be used for PackedListModule.code_id_hash, IsModuleListed() and logging
// APIs. // APIs.
std::string GetFingerprintString(uint32_t image_size, uint32_t time_data_stamp); std::string GetFingerprintString(uint32_t time_data_stamp, uint32_t image_size);
// These structs are directly written to a storage type. Therefore the padding // These structs are directly written to a storage type. Therefore the padding
// should be consistent across compilations. // should be consistent across compilations.
......
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