| // Copyright 2013 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 "remoting/host/pairing_registry_delegate_win.h" |
| |
| #include <utility> |
| |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/logging.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "base/win/registry.h" |
| |
| #include <windows.h> |
| |
| namespace remoting { |
| |
| namespace { |
| |
| // Duplicates a registry key handle (returned by RegCreateXxx/RegOpenXxx). |
| // The returned handle cannot be inherited and has the same permissions as |
| // the source one. |
| bool DuplicateKeyHandle(HKEY source, base::win::RegKey* dest) { |
| HANDLE handle; |
| if (!DuplicateHandle(GetCurrentProcess(), |
| source, |
| GetCurrentProcess(), |
| &handle, |
| 0, |
| FALSE, |
| DUPLICATE_SAME_ACCESS)) { |
| PLOG(ERROR) << "Failed to duplicate a registry key handle"; |
| return false; |
| } |
| |
| dest->Set(reinterpret_cast<HKEY>(handle)); |
| return true; |
| } |
| |
| // Reads value |value_name| from |key| as a JSON string and returns it as |
| // |base::Value|. |
| std::unique_ptr<base::DictionaryValue> ReadValue(const base::win::RegKey& key, |
| const wchar_t* value_name) { |
| // presubmit: allow wstring |
| std::wstring value_json; |
| LONG result = key.ReadValue(value_name, &value_json); |
| if (result != ERROR_SUCCESS) { |
| SetLastError(result); |
| PLOG(ERROR) << "Cannot read value '" << value_name << "'"; |
| return nullptr; |
| } |
| |
| // Parse the value. |
| std::string value_json_utf8 = base::WideToUTF8(value_json); |
| JSONStringValueDeserializer deserializer(value_json_utf8); |
| int error_code; |
| std::string error_message; |
| std::unique_ptr<base::Value> value = |
| deserializer.Deserialize(&error_code, &error_message); |
| if (!value) { |
| LOG(ERROR) << "Failed to parse '" << value_name << "': " << error_message |
| << " (" << error_code << ")."; |
| return nullptr; |
| } |
| |
| if (!value->is_dict()) { |
| LOG(ERROR) << "Failed to parse '" << value_name << "': not a dictionary."; |
| return nullptr; |
| } |
| |
| return base::DictionaryValue::From(std::move(value)); |
| } |
| |
| // Serializes |value| into a JSON string and writes it as value |value_name| |
| // under |key|. |
| bool WriteValue(base::win::RegKey& key, |
| const wchar_t* value_name, |
| std::unique_ptr<base::DictionaryValue> value) { |
| std::string value_json_utf8; |
| JSONStringValueSerializer serializer(&value_json_utf8); |
| if (!serializer.Serialize(*value)) { |
| LOG(ERROR) << "Failed to serialize '" << value_name << "'"; |
| return false; |
| } |
| |
| // presubmit: allow wstring |
| std::wstring value_json = base::UTF8ToWide(value_json_utf8); |
| LONG result = key.WriteValue(value_name, value_json.c_str()); |
| if (result != ERROR_SUCCESS) { |
| SetLastError(result); |
| PLOG(ERROR) << "Cannot write value '" << value_name << "'"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| using protocol::PairingRegistry; |
| |
| PairingRegistryDelegateWin::PairingRegistryDelegateWin() { |
| } |
| |
| PairingRegistryDelegateWin::~PairingRegistryDelegateWin() { |
| } |
| |
| bool PairingRegistryDelegateWin::SetRootKeys(HKEY privileged, |
| HKEY unprivileged) { |
| DCHECK(!privileged_.Valid()); |
| DCHECK(!unprivileged_.Valid()); |
| DCHECK(unprivileged); |
| |
| if (!DuplicateKeyHandle(unprivileged, &unprivileged_)) |
| return false; |
| |
| if (privileged) { |
| if (!DuplicateKeyHandle(privileged, &privileged_)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::unique_ptr<base::ListValue> PairingRegistryDelegateWin::LoadAll() { |
| std::unique_ptr<base::ListValue> pairings(new base::ListValue()); |
| |
| // Enumerate and parse all values under the unprivileged key. |
| DWORD count = unprivileged_.GetValueCount(); |
| for (DWORD index = 0; index < count; ++index) { |
| // presubmit: allow wstring |
| std::wstring value_name; |
| LONG result = unprivileged_.GetValueNameAt(index, &value_name); |
| if (result != ERROR_SUCCESS) { |
| SetLastError(result); |
| PLOG(ERROR) << "Cannot get the name of value " << index; |
| continue; |
| } |
| |
| PairingRegistry::Pairing pairing = Load(base::WideToUTF8(value_name)); |
| if (pairing.is_valid()) |
| pairings->Append(pairing.ToValue()); |
| } |
| |
| return pairings; |
| } |
| |
| bool PairingRegistryDelegateWin::DeleteAll() { |
| if (!privileged_.Valid()) { |
| LOG(ERROR) << "Cannot delete pairings: the delegate is read-only."; |
| return false; |
| } |
| |
| // Enumerate and delete the values in the privileged and unprivileged keys |
| // separately in case they get out of sync. |
| bool success = true; |
| DWORD count = unprivileged_.GetValueCount(); |
| while (count > 0) { |
| // presubmit: allow wstring |
| std::wstring value_name; |
| LONG result = unprivileged_.GetValueNameAt(0, &value_name); |
| if (result == ERROR_SUCCESS) |
| result = unprivileged_.DeleteValue(value_name.c_str()); |
| |
| success = success && (result == ERROR_SUCCESS); |
| count = unprivileged_.GetValueCount(); |
| } |
| |
| count = privileged_.GetValueCount(); |
| while (count > 0) { |
| // presubmit: allow wstring |
| std::wstring value_name; |
| LONG result = privileged_.GetValueNameAt(0, &value_name); |
| if (result == ERROR_SUCCESS) |
| result = privileged_.DeleteValue(value_name.c_str()); |
| |
| success = success && (result == ERROR_SUCCESS); |
| count = privileged_.GetValueCount(); |
| } |
| |
| return success; |
| } |
| |
| PairingRegistry::Pairing PairingRegistryDelegateWin::Load( |
| const std::string& client_id) { |
| // presubmit: allow wstring |
| std::wstring value_name = base::UTF8ToWide(client_id); |
| |
| // Read unprivileged fields first. |
| std::unique_ptr<base::DictionaryValue> pairing = |
| ReadValue(unprivileged_, value_name.c_str()); |
| if (!pairing) |
| return PairingRegistry::Pairing(); |
| |
| // Read the shared secret. |
| if (privileged_.Valid()) { |
| std::unique_ptr<base::DictionaryValue> secret = |
| ReadValue(privileged_, value_name.c_str()); |
| if (!secret) |
| return PairingRegistry::Pairing(); |
| |
| // Merge the two dictionaries. |
| pairing->MergeDictionary(secret.get()); |
| } |
| |
| return PairingRegistry::Pairing::CreateFromValue(*pairing); |
| } |
| |
| bool PairingRegistryDelegateWin::Save(const PairingRegistry::Pairing& pairing) { |
| if (!privileged_.Valid()) { |
| LOG(ERROR) << "Cannot save pairing entry '" << pairing.client_id() |
| << "': the pairing registry privileged key is invalid."; |
| return false; |
| } |
| |
| // Convert pairing to JSON. |
| std::unique_ptr<base::DictionaryValue> pairing_json = pairing.ToValue(); |
| |
| // Extract the shared secret to a separate dictionary. |
| std::unique_ptr<base::Value> secret_key; |
| CHECK(pairing_json->Remove(PairingRegistry::kSharedSecretKey, &secret_key)); |
| std::unique_ptr<base::DictionaryValue> secret_json( |
| new base::DictionaryValue()); |
| secret_json->Set(PairingRegistry::kSharedSecretKey, std::move(secret_key)); |
| |
| // presubmit: allow wstring |
| std::wstring value_name = base::UTF8ToWide(pairing.client_id()); |
| |
| // Write pairing to the registry. |
| if (!WriteValue(privileged_, value_name.c_str(), std::move(secret_json)) || |
| !WriteValue(unprivileged_, value_name.c_str(), std::move(pairing_json))) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool PairingRegistryDelegateWin::Delete(const std::string& client_id) { |
| if (!privileged_.Valid()) { |
| LOG(ERROR) << "Cannot delete pairing entry '" << client_id |
| << "': the delegate is read-only."; |
| return false; |
| } |
| |
| // presubmit: allow wstring |
| std::wstring value_name = base::UTF8ToWide(client_id); |
| LONG result = privileged_.DeleteValue(value_name.c_str()); |
| if (result != ERROR_SUCCESS && |
| result != ERROR_FILE_NOT_FOUND && |
| result != ERROR_PATH_NOT_FOUND) { |
| SetLastError(result); |
| PLOG(ERROR) << "Cannot delete pairing entry '" << client_id << "'"; |
| return false; |
| } |
| |
| result = unprivileged_.DeleteValue(value_name.c_str()); |
| if (result != ERROR_SUCCESS && |
| result != ERROR_FILE_NOT_FOUND && |
| result != ERROR_PATH_NOT_FOUND) { |
| SetLastError(result); |
| PLOG(ERROR) << "Cannot delete pairing entry '" << client_id << "'"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::unique_ptr<PairingRegistry::Delegate> CreatePairingRegistryDelegate() { |
| return std::make_unique<PairingRegistryDelegateWin>(); |
| } |
| |
| } // namespace remoting |