blob: 5a057abacee50b83f0ba6f77db4f610ae56f3baa [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/chrome_cleaner/pup_data/pup_data.h"
#include <algorithm>
#include <utility>
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "chrome/chrome_cleaner/proto/shared_pup_enums.pb.h"
#include "chrome/chrome_cleaner/pup_data/uws_catalog.h"
namespace chrome_cleaner {
namespace {
// TODO(csharp): Handle the SysWOW64 case if needed.
const wchar_t kGroupPolicyPath[] = L"%SystemRoot%\\system32\\GroupPolicy";
const wchar_t kMachinePolicyFolder[] = L"Machine";
const wchar_t kUserPolicyFolder[] = L"User";
// This UwSId is used to distinguish Urza PUPs from external engines' PUPs.
const UwSId kMaxValidUrzaUwSId = 9999;
} // namespace
// static
const wchar_t PUPData::kCommaDelimiter[] = L",";
const size_t PUPData::kCommaDelimiterLength =
base::size(PUPData::kCommaDelimiter) - 1;
const wchar_t PUPData::kCommonDelimiters[] = L" ,\0";
const size_t PUPData::kCommonDelimitersLength =
base::size(PUPData::kCommonDelimiters) - 1;
// The escape character used for registry key name and value is an unused
// unicode character (see: http://en.wikipedia.org/wiki/Private_Use_Areas).
const wchar_t PUPData::kRegistryPatternEscapeCharacter = L'\uFFFF';
// TODO(csharp): Find a way to add a compile assert making sure that the \uFFFF
// used here is the same as the one used in ESCAPE_REGISTRY_STR(str).
// static
const char PUPData::kRemovedPUPNamePrefix[] = "Removed/";
const char PUPData::kObservedPUPNamePrefix[] = "Observed/";
// static
PUPData::PUPDataMap* PUPData::cached_pup_map_ = nullptr;
std::vector<UwSId>* PUPData::cached_uws_ids_ = nullptr;
PUPData::UwSCatalogs* PUPData::last_uws_catalogs_ = nullptr;
PUPData::RegistryFootprint::RegistryFootprint()
: rule(REGISTRY_VALUE_MATCH_INVALID) {}
PUPData::RegistryFootprint::RegistryFootprint(
const RegKeyPath& key_path,
const base::string16& value_name,
const base::string16& value_substring,
RegistryMatchRule rule)
: key_path(key_path),
value_name(value_name),
value_substring(value_substring),
rule(rule) {}
PUPData::RegistryFootprint::RegistryFootprint(const RegistryFootprint& other) =
default;
PUPData::RegistryFootprint::~RegistryFootprint() = default;
PUPData::RegistryFootprint& PUPData::RegistryFootprint::operator=(
const PUPData::RegistryFootprint& other) = default;
bool PUPData::RegistryFootprint::operator==(
const RegistryFootprint& other) const {
return key_path == other.key_path && value_name == other.value_name &&
value_substring == other.value_substring && rule == other.rule;
}
PUPData::FileInfo::FileInfo() = default;
PUPData::FileInfo::FileInfo(const PUPData::FileInfo&) = default;
PUPData::FileInfo::~FileInfo() = default;
PUPData::FileInfo::FileInfo(const std::set<UwS::TraceLocation>& found_in)
: found_in(found_in) {}
bool PUPData::FileInfo::operator==(const PUPData::FileInfo& other) const {
return found_in == other.found_in;
}
PUPData::PUP::PUP() : signature_(nullptr) {}
PUPData::PUP::PUP(const UwSSignature* const signature)
: signature_(signature) {}
PUPData::PUP::PUP(const PUPData::PUP& other) = default;
PUPData::PUP::~PUP() = default;
PUPData::PUP& PUPData::PUP::operator=(const PUPData::PUP& other) = default;
bool PUPData::PUP::AddDiskFootprint(const base::FilePath& file_path) {
return expanded_disk_footprints.Insert(file_path);
}
void PUPData::PUP::ClearDiskFootprints() {
expanded_disk_footprints.clear();
}
void PUPData::PUP::AddDiskFootprintTraceLocation(const base::FilePath& path,
UwS::TraceLocation location) {
PUPData::FileInfo* existing_info = disk_footprints_info.Find(path);
if (existing_info)
existing_info->found_in.insert(location);
else
disk_footprints_info.Insert(path, PUPData::FileInfo({location}));
}
void PUPData::PUP::MergeFrom(const PUPData::PUP& other) {
expanded_disk_footprints.CopyFrom(other.expanded_disk_footprints);
expanded_registry_footprints.insert(
expanded_registry_footprints.end(),
other.expanded_registry_footprints.begin(),
other.expanded_registry_footprints.end());
expanded_scheduled_tasks.insert(expanded_scheduled_tasks.end(),
other.expanded_scheduled_tasks.begin(),
other.expanded_scheduled_tasks.end());
for (auto path_locations_it = other.disk_footprints_info.map().begin();
path_locations_it != other.disk_footprints_info.map().end();
++path_locations_it) {
const base::FilePath& path = path_locations_it->first;
const PUPData::FileInfo& other_info = path_locations_it->second;
PUPData::FileInfo* existing_locations = disk_footprints_info.Find(path);
if (existing_locations) {
existing_locations->found_in.insert(other_info.found_in.begin(),
other_info.found_in.end());
} else {
disk_footprints_info.Insert(path, other_info);
}
}
}
PUPData::PUPData() : has_filter_(false) {
DCHECK(kPUPs);
DCHECK(kObservedPUPs);
DCHECK(kRemovedPUPs);
}
PUPData::~PUPData() = default;
// static
bool PUPData::IsKnownPUP(UwSId uws_id) {
DCHECK(cached_pup_map_);
return cached_pup_map_->find(uws_id) != cached_pup_map_->end();
}
// static
PUPData::PUP* PUPData::GetPUP(UwSId uws_id) {
DCHECK(cached_pup_map_);
auto it = cached_pup_map_->find(uws_id);
// Because of this check, tests that override PUP data will break if |uws_id|
// corresponds to a deprecated UwS.
// TODO(csharp): Make tests more resilient to the existence of PUP IDs.
if (it == cached_pup_map_->end())
NOTREACHED() << "Unknown pup id requested: " << uws_id;
return it->second.get();
}
// static
void PUPData::InitializePUPData(const UwSCatalogs& uws_catalogs) {
// Reinitialize the caches if they already exist.
delete cached_pup_map_;
cached_pup_map_ = nullptr;
delete cached_uws_ids_;
cached_uws_ids_ = nullptr;
UpdateCachedUwS();
if (!last_uws_catalogs_)
last_uws_catalogs_ = new UwSCatalogs();
last_uws_catalogs_->clear();
for (const UwSCatalog* uws_catalog : uws_catalogs) {
last_uws_catalogs_->push_back(uws_catalog);
for (const UwSId uws_id : uws_catalog->GetUwSIds()) {
if (!uws_catalog->IsEnabledForScanning(uws_id))
continue;
PUPData::AddPUPToMap(uws_catalog->CreatePUPForId(uws_id));
}
}
}
// static
const std::vector<UwSId>* PUPData::GetUwSIds() {
DCHECK(cached_uws_ids_);
return cached_uws_ids_;
}
// static
const char* PUPData::GetPUPName(const PUP* pup) {
DCHECK(pup);
return pup->signature().name ? pup->signature().name : R"(???)";
}
// static
bool PUPData::HasReportOnlyFlag(Flags flags) {
return !HasRemovalFlag(flags);
}
// static
bool PUPData::HasRemovalFlag(Flags flags) {
return (flags & FLAGS_ACTION_REMOVE) != 0;
}
// static
bool PUPData::HasRebootFlag(Flags flags) {
return (flags & FLAGS_REMOVAL_FORCE_REBOOT) != 0;
}
// static
bool PUPData::HasConfirmedUwSFlag(Flags flags) {
return (flags & FLAGS_STATE_CONFIRMED_UWS) != 0;
}
// static
bool PUPData::IsReportOnlyUwS(UwSId uws_id) {
return HasReportOnlyFlag(GetPUP(uws_id)->signature().flags);
}
// static
bool PUPData::IsRemovable(UwSId uws_id) {
return HasRemovalFlag(GetPUP(uws_id)->signature().flags);
}
// static
bool PUPData::IsConfirmedUwS(UwSId uws_id) {
return HasConfirmedUwSFlag(GetPUP(uws_id)->signature().flags);
}
// static
void PUPData::ChoosePUPs(const std::vector<UwSId>& input_pup_list,
bool (*chooser)(Flags),
std::vector<UwSId>* output) {
DCHECK(output);
DCHECK(chooser);
for (const auto& uws_id : input_pup_list) {
const PUPData::UwSSignature& signature = GetPUP(uws_id)->signature();
if (chooser(signature.flags))
output->push_back(signature.id);
}
}
// static
bool PUPData::HasFlaggedPUP(const std::vector<UwSId>& input_pup_list,
bool (*chooser)(Flags)) {
DCHECK(chooser);
for (const auto& uws_id : input_pup_list) {
const auto* pup = GetPUP(uws_id);
if (chooser(pup->signature().flags))
return true;
}
return false;
}
// static
FilePathSet PUPData::GetFilesDetectedInServices(
const std::vector<UwSId>& uws_list) {
FilePathSet detected_in_services;
for (chrome_cleaner::UwSId uws_id : uws_list) {
const PUP* uws = GetPUP(uws_id);
for (auto path_location_it = uws->disk_footprints_info.map().begin();
path_location_it != uws->disk_footprints_info.map().end();
++path_location_it) {
const std::set<UwS::TraceLocation>& found_in =
path_location_it->second.found_in;
if (found_in.find(UwS::FOUND_IN_SERVICE) != found_in.end())
detected_in_services.Insert(path_location_it->first);
}
}
return detected_in_services;
}
// static
bool PUPData::GetRootKeyFromRegistryRoot(RegistryRoot registry_root,
HKEY* key,
base::FilePath* policy_file) {
DCHECK(key);
HKEY hkroot = nullptr;
switch (registry_root) {
case (REGISTRY_ROOT_LOCAL_MACHINE):
hkroot = HKEY_LOCAL_MACHINE;
break;
case (REGISTRY_ROOT_USERS):
hkroot = HKEY_CURRENT_USER;
break;
case (REGISTRY_ROOT_CLASSES):
hkroot = HKEY_CLASSES_ROOT;
break;
case (REGISTRY_ROOT_MACHINE_GROUP_POLICY):
hkroot = HKEY_LOCAL_MACHINE;
if (policy_file) {
*policy_file =
base::FilePath(kGroupPolicyPath).Append(kMachinePolicyFolder);
}
break;
case (REGISTRY_ROOT_USERS_GROUP_POLICY):
hkroot = HKEY_CURRENT_USER;
if (policy_file) {
*policy_file =
base::FilePath(kGroupPolicyPath).Append(kUserPolicyFolder);
}
break;
case (REGISTRY_ROOT_INVALID):
return false;
}
*key = hkroot;
return true;
}
// static.
void PUPData::DeleteRegistryKey(const RegKeyPath& key_path, PUPData::PUP* pup) {
DCHECK(pup);
RegistryFootprint footprint = {};
footprint.key_path = key_path;
footprint.rule = REGISTRY_VALUE_MATCH_KEY;
pup->expanded_registry_footprints.push_back(footprint);
}
// static.
void PUPData::DeleteRegistryKeyIfPresent(const RegKeyPath& key_path,
PUPData::PUP* pup) {
if (key_path.Exists())
PUPData::DeleteRegistryKey(key_path, pup);
}
// static.
void PUPData::DeleteRegistryValue(const RegKeyPath& key_path,
const wchar_t* value_name,
PUPData::PUP* pup) {
DCHECK(value_name);
DCHECK(pup);
RegistryFootprint footprint = {};
footprint.key_path = key_path;
footprint.value_name = value_name;
footprint.rule = REGISTRY_VALUE_MATCH_VALUE_NAME;
pup->expanded_registry_footprints.push_back(footprint);
}
// static.
void PUPData::UpdateRegistryValue(const RegKeyPath& key_path,
const wchar_t* value_name,
const wchar_t* value_substring,
RegistryMatchRule rule,
PUPData::PUP* pup) {
DCHECK(pup);
RegistryFootprint footprint = {};
footprint.key_path = key_path;
DCHECK(value_name);
footprint.value_name = value_name;
DCHECK(value_substring);
footprint.value_substring = value_substring;
footprint.rule = rule;
pup->expanded_registry_footprints.push_back(footprint);
}
// static.
void PUPData::DeleteScheduledTask(const wchar_t* task_name, PUPData::PUP* pup) {
DCHECK(task_name);
DCHECK(pup);
pup->expanded_scheduled_tasks.push_back(task_name);
}
// static
const PUPData::PUPDataMap* PUPData::GetAllPUPs() {
DCHECK(cached_pup_map_);
return cached_pup_map_;
}
// static
void PUPData::UpdateCachedUwS() {
if (!cached_pup_map_)
cached_pup_map_ = new PUPDataMap();
if (!cached_uws_ids_)
cached_uws_ids_ = new std::vector<UwSId>;
PUPData::AddUwSSignaturesToMap(kPUPs);
PUPData::AddUwSSignaturesToMap(kObservedPUPs);
PUPData::AddUwSSignaturesToMap(kRemovedPUPs);
// TODO(joenotcharles): This does not handle changes to UwS loaded from
// catalogs. Right now that works because UpdateCachedUwS is only called from
// InitializePUPData (which adds UwS from the catalogs after this returns)
// and TestPUPData, which does not deal with catalogs.
}
// static
void PUPData::AddPUPToMap(std::unique_ptr<PUPData::PUP> pup) {
const UwSId id = pup->signature().id;
DCHECK(cached_pup_map_->find(id) == cached_pup_map_->end());
cached_pup_map_->insert(std::make_pair(id, std::move(pup)));
cached_uws_ids_->push_back(id);
}
// static
void PUPData::AddUwSSignaturesToMap(
const PUPData::UwSSignature* signature_array) {
const PUPData::UwSSignature* signature = signature_array;
while (signature->id != kInvalidUwSId) {
auto cached_pup_iter = cached_pup_map_->find(signature->id);
if (cached_pup_iter == cached_pup_map_->end())
AddPUPToMap(std::make_unique<PUPData::PUP>(signature));
else
cached_pup_iter->second->signature_ = signature;
++signature;
}
}
// static
Engine::Name PUPData::GetEngine(UwSId id) {
if (id <= kMaxValidUrzaUwSId)
return Engine::URZA;
else
return Engine::ESET;
}
} // namespace chrome_cleaner