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..ca2d7fe9c1cedf6f71b3a214dbd9f71be1600813 |
--- /dev/null |
+++ b/chrome/installer/setup/user_hive_visitor.cc |
@@ -0,0 +1,166 @@ |
+// 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)); |
+ subkey_name_ = ShellUtil::ByteArrayToBase32(&buffer[0], arraysize(buffer)); |
+ 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. |
+ base::string16 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; |
+ } |
+ base::string16 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 |