Chromium Code Reviews| Index: chrome/installer/setup/user_hive_visitor.cc |
| diff --git a/chrome/installer/setup/user_hive_visitor.cc b/chrome/installer/setup/user_hive_visitor.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8dc54f139d7fa3337692a7eb8b437afd81a6f754 |
| --- /dev/null |
| +++ b/chrome/installer/setup/user_hive_visitor.cc |
| @@ -0,0 +1,170 @@ |
| +// Copyright 2016 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/installer/setup/user_hive_visitor.h" |
| + |
| +#include <utility> |
| + |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/callback_helpers.h" |
| +#include "base/files/file_path.h" |
| +#include "base/files/file_util.h" |
| +#include "base/logging.h" |
| +#include "base/rand_util.h" |
| +#include "base/strings/string16.h" |
| +#include "base/strings/string_piece.h" |
| +#include "base/win/registry.h" |
| +#include "chrome/installer/setup/setup_util.h" |
| +#include "chrome/installer/util/shell_util.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(); |
| + |
| + // 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. |
| + base::string16 subkey_name_; |
| + |
| + // The loaded key. |
| + base::win::RegKey key_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ScopedUserHive); |
| +}; |
| + |
| +ScopedUserHive::ScopedUserHive(const base::FilePath& hive_file) { |
| + // Generate a random name for the key at which the file will be loaded. |
| + uint8_t buffer[10] = {}; |
| + base::RandBytes(&buffer[0], arraysize(buffer)); |
| + base::string16 name = |
| + ShellUtil::ByteArrayToBase32(&buffer[0], arraysize(buffer)); |
| + DCHECK_EQ(16U, name.size()); |
| + |
| + LONG result = |
| + ::RegLoadKey(HKEY_LOCAL_MACHINE, name.c_str(), hive_file.value().c_str()); |
| + if (result != ERROR_SUCCESS) { |
| + ::SetLastError(result); |
| + PLOG(ERROR) << "Failed loading user hive file \"" << hive_file.value() |
| + << "\""; |
| + return; |
| + } |
| + subkey_name_ = std::move(name); |
| + |
| + // 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) |
| + return; |
| + ::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"; |
| + constexpr wchar_t kProfileImagePathValue[] = L"ProfileImagePath"; |
| + |
| + // 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(); |
| + VLOG(1) << "Found local account \"" << sid << "\"."; |
| + base::string16 profile_key_name(kProfileListKey); |
| + profile_key_name.append(1, L'\\').append(sid); |
| + base::win::RegKey key; |
| + 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; |
| + } |
| + |
| + // Read the path to the profile directory. |
| + base::string16 image_path; |
| + result = key.ReadValue(kProfileImagePathValue, &image_path); |
|
gab
2016/06/01 14:34:44
I'd expect keeping L"ProfileImagePath" inline here
grt (UTC plus 2)
2016/06/01 14:40:38
Done.
|
| + if (result != ERROR_SUCCESS) { |
| + ::SetLastError(result); |
| + PLOG(ERROR) << "Failed reading ProfileImagePath value of \"" |
| + << profile_key_name << "\""; |
| + } |
| + |
| + // First try to access the user hive pre-mounted by the OS. |
| + key.Close(); |
| + VLOG(1) << "Checking for pre-loaded hive for sid \"" << sid << "\"."; |
| + if (OpenUserHive(sid, &key)) { |
| + VLOG(1) << "Found loaded hive for sid \"" << sid << "\""; |
| + if (!visitor.Run(sid, &key)) |
| + break; |
| + continue; |
| + } |
| + |
| + // Failing that, try loading the hive manually. The path will be empty if |
| + // not present in the registry. Skip this profile in that case. |
| + 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 |