blob: d2423c836e2d25c00e58f9b7e866924603ae3650 [file] [log] [blame]
// 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/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/registry.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->IsType(base::Value::TYPE_DICTIONARY)) {
LOG(ERROR) << "Failed to parse '" << value_name << "': not a dictionary.";
return nullptr;
}
return base::WrapUnique(static_cast<base::DictionaryValue*>(value.release()));
}
// 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().release());
}
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, secret_key.release());
// 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 base::WrapUnique(new PairingRegistryDelegateWin());
}
} // namespace remoting