// Copyright 2016 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/installer/setup/user_hive_visitor.h"
#include <string>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/installer/util/scoped_token_privilege.h"
#include "components/base32/base32.h"
namespace installer {
namespace {
// A helper for loading and opening a hive into a random subkey of
// HKEY_LOCAL_MACHINE.
class ScopedUserHive {
public:
explicit ScopedUserHive(const base::FilePath& hive_file);
ScopedUserHive(const ScopedUserHive&) = delete;
ScopedUserHive& operator=(const ScopedUserHive&) = delete;
~ScopedUserHive();
// Returns true if the hive file was loaded.
bool valid() const { return key_.Valid(); }
// Returns the key at the root of the loaded hive, or nullptr if not valid.
base::win::RegKey* key() { return key_.Valid() ? &key_ : nullptr; }
private:
// The randomly-chosen name of the subkey under HKLM where the file is loaded.
// If empty, the file is not loaded.
std::wstring subkey_name_;
// The loaded key.
base::win::RegKey key_;
};
ScopedUserHive::ScopedUserHive(const base::FilePath& hive_file) {
// Generate a random name for the key at which the file will be loaded.
subkey_name_ = base::ASCIIToWide(base32::Base32Encode(
base::RandBytesAsVector(10), base32::Base32EncodePolicy::OMIT_PADDING));
DCHECK_EQ(16U, subkey_name_.size());
LONG result = ::RegLoadKey(HKEY_LOCAL_MACHINE, subkey_name_.c_str(),
hive_file.value().c_str());
if (result != ERROR_SUCCESS) {
// Clear subkey_name_ since the load failed so that an unload will not be
// attempted in the dtor.
subkey_name_.clear();
::SetLastError(result);
PLOG(ERROR) << "Failed loading user hive file \"" << hive_file.value()
<< "\"";
return;
}
// Open the newly-loaded key.
result = key_.Open(HKEY_LOCAL_MACHINE, subkey_name_.c_str(), KEY_ALL_ACCESS);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed opening loaded hive file \"" << hive_file.value()
<< "\"";
}
}
ScopedUserHive::~ScopedUserHive() {
key_.Close();
if (subkey_name_.empty())
return;
LONG result = ::RegUnLoadKey(HKEY_LOCAL_MACHINE, subkey_name_.c_str());
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed unloading user hive at \"" << subkey_name_ << "\"";
}
}
bool OpenUserHive(const wchar_t* sid, base::win::RegKey* user_hive) {
DCHECK(user_hive);
LONG result = user_hive->Open(HKEY_USERS, sid, KEY_ALL_ACCESS);
if (result == ERROR_SUCCESS)
return true;
if (result == ERROR_FILE_NOT_FOUND) {
VLOG(1) << "Hive is not loaded for user \"" << sid << "\"";
return false;
}
::SetLastError(result);
PLOG(ERROR) << "Failed opening hive for user \"" << sid << "\"";
return false;
}
} // namespace
void VisitUserHives(const HiveVisitor& visitor) {
constexpr wchar_t kProfileListKey[] =
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";
// Privileges required to load a registry hive file.
ScopedTokenPrivilege se_backup_name_privilege(SE_BACKUP_NAME);
ScopedTokenPrivilege se_restore_name_privilege(SE_RESTORE_NAME);
for (base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, kProfileListKey);
iter.Valid(); ++iter) {
const wchar_t* sid = iter.Name();
// First try to access the user hive pre-mounted by the OS.
VLOG(1) << "Checking for pre-loaded hive for local account \"" << sid
<< "\"";
base::win::RegKey key;
if (OpenUserHive(sid, &key)) {
VLOG(1) << "Found loaded hive for sid \"" << sid << "\"";
if (!visitor.Run(sid, &key))
break;
continue;
}
// Read the path to the profile directory to load the hive manually.
std::wstring profile_key_name(kProfileListKey);
profile_key_name.append(1, L'\\').append(sid);
LONG result =
key.Open(HKEY_LOCAL_MACHINE, profile_key_name.c_str(), KEY_QUERY_VALUE);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed opening profile key \"" << profile_key_name
<< "\"";
continue;
}
std::wstring image_path;
result = key.ReadValue(L"ProfileImagePath", &image_path);
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed reading ProfileImagePath value of \""
<< profile_key_name << "\"";
}
key.Close();
if (image_path.empty())
continue;
base::FilePath hive_file(
base::FilePath(image_path).Append(FILE_PATH_LITERAL("ntuser.dat")));
VLOG(1) << "Falling back to opening \"" << hive_file.value() << "\"";
if (!base::PathExists(hive_file)) {
VPLOG(1) << "Hive file not found or inaccessible \"" << hive_file.value()
<< "\"";
continue;
}
ScopedUserHive user_hive(hive_file);
if (user_hive.valid()) {
VLOG(1) << "Loaded and opened hive for sid \"" << sid << "\"";
if (!visitor.Run(sid, user_hive.key()))
break;
}
}
}
} // namespace installer