1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
|
// Copyright 2017 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/browser/win/conflicts/installed_applications.h"
#include <algorithm>
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/registry.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "chrome/browser/win/conflicts/msi_util.h"
namespace {
// Returns true if |candidate| is registered as a system component.
bool IsSystemComponent(const base::win::RegKey& candidate) {
DWORD system_component = 0;
return candidate.ReadValueDW(L"SystemComponent", &system_component) ==
ERROR_SUCCESS &&
system_component == 1;
}
// Fetches a string |value| out of |key|. Return false if a non-empty value
// could not be retrieved.
bool GetValue(const base::win::RegKey& key,
const wchar_t* value,
std::wstring* result) {
return key.ReadValue(value, result) == ERROR_SUCCESS && !result->empty();
}
// Try to get the |install_path| from |candidate| using the InstallLocation
// value. Return true on success.
bool GetInstallPathUsingInstallLocation(const base::win::RegKey& candidate,
base::FilePath* install_path) {
std::wstring install_location;
if (GetValue(candidate, L"InstallLocation", &install_location)) {
*install_path = base::FilePath(std::move(install_location));
return true;
}
return false;
}
// Returns true if the |component_path| points to a registry key. Registry key
// paths are characterized by a number instead of a drive letter.
// See the documentation for ::MsiGetComponentPath():
// https://msdn.microsoft.com/library/windows/desktop/aa370112.aspx
bool IsRegistryComponentPath(const std::wstring& component_path) {
std::wstring drive_letter =
component_path.substr(0, component_path.find(':'));
for (const wchar_t* registry_drive_letter :
{L"00", L"01", L"02", L"03", L"20", L"21", L"22", L"23"}) {
if (drive_letter == registry_drive_letter)
return true;
}
return false;
}
// Returns all the files installed by the product identified by |product_guid|.
// Returns true on success.
bool GetInstalledFilesUsingMsiGuid(
const std::wstring& product_guid,
const MsiUtil& msi_util,
const std::wstring& user_sid,
std::vector<base::FilePath>* installed_files) {
// An invalid product guid may have been passed to this function. In this
// case, GetMsiComponentPaths() will return false so it is not necessary to
// specifically filter those out.
std::vector<std::wstring> component_paths;
if (!msi_util.GetMsiComponentPaths(product_guid, user_sid, &component_paths))
return false;
for (auto& component_path : component_paths) {
// Exclude registry component paths.
if (IsRegistryComponentPath(component_path))
continue;
installed_files->push_back(base::FilePath(std::move(component_path)));
}
return true;
}
// Helper function to sort |container| using CompareLessIgnoreCase.
void SortByFilePaths(
std::vector<std::pair<base::FilePath, size_t>>* container) {
std::sort(container->begin(), container->end(),
[](const auto& lhs, const auto& rhs) {
return base::FilePath::CompareLessIgnoreCase(lhs.first.value(),
rhs.first.value());
});
}
} // namespace
InstalledApplications::InstalledApplications()
: InstalledApplications(std::make_unique<MsiUtil>()) {}
InstalledApplications::~InstalledApplications() = default;
bool InstalledApplications::GetInstalledApplications(
const base::FilePath& file,
std::vector<ApplicationInfo>* applications) const {
// First, check if an exact file match exists in the installed files list.
if (GetApplicationsFromInstalledFiles(file, applications))
return true;
// Then try to find a parent directory in the install directories list.
return GetApplicationsFromInstallDirectories(file, applications);
}
InstalledApplications::InstalledApplications(
std::unique_ptr<MsiUtil> msi_util) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
// Iterate over all the variants of the uninstall registry key.
static constexpr wchar_t kUninstallKeyPath[] =
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// Retrieve the current user's Security Identifier. If it fails, |user_sid|
// will stay empty.
std::wstring user_sid;
base::win::GetUserSidString(&user_sid);
for (const auto& combination : GenRegistryKeyCombinations()) {
for (base::win::RegistryKeyIterator i(combination.first, kUninstallKeyPath,
combination.second);
i.Valid(); ++i) {
CheckRegistryKeyForInstalledApplication(
combination.first, kUninstallKeyPath, combination.second, i.Name(),
*msi_util, user_sid);
}
}
// The vectors are sorted so that binary searching can be used. No additional
// entries will be added anyways.
SortByFilePaths(&installed_files_);
SortByFilePaths(&install_directories_);
}
std::vector<std::pair<HKEY, REGSAM>>
InstalledApplications::GenRegistryKeyCombinations() const {
std::vector<std::pair<HKEY, REGSAM>> registry_key_combinations;
if (base::win::OSInfo::GetArchitecture() ==
base::win::OSInfo::X86_ARCHITECTURE) {
// On 32-bit Windows, there is only one view of the registry.
registry_key_combinations.emplace_back(HKEY_CURRENT_USER, 0);
registry_key_combinations.emplace_back(HKEY_LOCAL_MACHINE, 0);
} else {
// On 64-bit Windows, there also exists a 32-bit view (Wow6432Node). Except
// that the "HKCU\SOFTWARE\" subtree is shared between the 32-bits and
// 64 bits views. Accessing both would create duplicate entries.
// See https://msdn.microsoft.com/library/windows/desktop/aa384253.aspx
registry_key_combinations.emplace_back(HKEY_CURRENT_USER, 0);
registry_key_combinations.emplace_back(HKEY_LOCAL_MACHINE, KEY_WOW64_32KEY);
registry_key_combinations.emplace_back(HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY);
}
return registry_key_combinations;
}
void InstalledApplications::CheckRegistryKeyForInstalledApplication(
HKEY hkey,
const std::wstring& key_path,
REGSAM wow64access,
const std::wstring& key_name,
const MsiUtil& msi_util,
const std::wstring& user_sid) {
std::wstring candidate_key_path = base::StrCat({key_path, L"\\", key_name});
base::win::RegKey candidate(hkey, candidate_key_path.c_str(),
KEY_QUERY_VALUE | wow64access);
if (!candidate.Valid())
return;
// System components are not displayed in the Add or remove applications list.
if (IsSystemComponent(candidate))
return;
// If there is no UninstallString, the Uninstall button is grayed out.
std::wstring uninstall_string;
if (!GetValue(candidate, L"UninstallString", &uninstall_string))
return;
// Ignore Microsoft applications.
std::wstring publisher;
if (GetValue(candidate, L"Publisher", &publisher) &&
base::StartsWith(publisher, L"Microsoft", base::CompareCase::SENSITIVE)) {
return;
}
// Because this class is used to display a warning to the user, not having
// a display name renders the warning somewhat useless. Ignore those
// candidates.
std::wstring display_name;
if (!GetValue(candidate, L"DisplayName", &display_name))
return;
base::FilePath install_path;
if (GetInstallPathUsingInstallLocation(candidate, &install_path)) {
applications_.push_back({std::move(display_name), hkey,
std::move(candidate_key_path), wow64access});
const size_t application_index = applications_.size() - 1;
install_directories_.emplace_back(std::move(install_path),
application_index);
return;
}
std::vector<base::FilePath> installed_files;
if (GetInstalledFilesUsingMsiGuid(key_name, msi_util, user_sid,
&installed_files)) {
applications_.push_back({std::move(display_name), hkey,
std::move(candidate_key_path), wow64access});
const size_t application_index = applications_.size() - 1;
for (auto& installed_file : installed_files) {
installed_files_.emplace_back(std::move(installed_file),
application_index);
}
}
}
bool InstalledApplications::GetApplicationsFromInstalledFiles(
const base::FilePath& file,
std::vector<ApplicationInfo>* applications) const {
// This functor is used to find all exact items by their key in a collection
// of key/value pairs.
struct FilePathLess {
bool operator()(const std::pair<base::FilePath, size_t> element,
const base::FilePath& file) {
return base::FilePath::CompareLessIgnoreCase(element.first.value(),
file.value());
}
bool operator()(const base::FilePath& file,
const std::pair<base::FilePath, size_t> element) {
return base::FilePath::CompareLessIgnoreCase(file.value(),
element.first.value());
}
};
auto equal_range = std::equal_range(
installed_files_.begin(), installed_files_.end(), file, FilePathLess());
auto nb_matches = std::distance(equal_range.first, equal_range.second);
if (nb_matches == 0)
return false;
applications->reserve(applications->size() + nb_matches);
for (auto iter = equal_range.first; iter != equal_range.second; ++iter)
applications->push_back(applications_[iter->second]);
return true;
}
bool InstalledApplications::GetApplicationsFromInstallDirectories(
const base::FilePath& file,
std::vector<ApplicationInfo>* applications) const {
// This functor is used to find all matching items by their key in a
// collection of key/value pairs. This also takes advantage of the fact that
// only the first element of the pair is a directory.
struct FilePathParentLess {
bool operator()(const std::pair<base::FilePath, size_t> directory,
const base::FilePath& file) {
if (directory.first.IsParent(file))
return false;
return base::FilePath::CompareLessIgnoreCase(directory.first.value(),
file.value());
}
bool operator()(const base::FilePath& file,
const std::pair<base::FilePath, size_t> directory) {
if (directory.first.IsParent(file))
return false;
return base::FilePath::CompareLessIgnoreCase(file.value(),
directory.first.value());
}
};
auto equal_range =
std::equal_range(install_directories_.begin(), install_directories_.end(),
file, FilePathParentLess());
// Skip cases where there are multiple matches because there is no way to know
// which application is the real owner of the |file| with the information this
// class possess.
if (std::distance(equal_range.first, equal_range.second) != 1)
return false;
applications->push_back(applications_[equal_range.first->second]);
return true;
}
bool operator<(const InstalledApplications::ApplicationInfo& lhs,
const InstalledApplications::ApplicationInfo& rhs) {
return std::tie(lhs.name, lhs.registry_root, lhs.registry_key_path,
lhs.registry_wow64_access) <
std::tie(rhs.name, rhs.registry_root, rhs.registry_key_path,
rhs.registry_wow64_access);
}
|