1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/host/pairing_registry_delegate_win.h"
6
7#include "base/json/json_string_value_serializer.h"
8#include "base/logging.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/values.h"
11#include "base/win/registry.h"
12
13namespace remoting {
14
15namespace {
16
17// Duplicates a registry key handle (returned by RegCreateXxx/RegOpenXxx).
18// The returned handle cannot be inherited and has the same permissions as
19// the source one.
20bool DuplicateKeyHandle(HKEY source, base::win::RegKey* dest) {
21  HANDLE handle;
22  if (!DuplicateHandle(GetCurrentProcess(),
23                       source,
24                       GetCurrentProcess(),
25                       &handle,
26                       0,
27                       FALSE,
28                       DUPLICATE_SAME_ACCESS)) {
29    PLOG(ERROR) << "Failed to duplicate a registry key handle";
30    return false;
31  }
32
33  dest->Set(reinterpret_cast<HKEY>(handle));
34  return true;
35}
36
37// Reads value |value_name| from |key| as a JSON string and returns it as
38// |base::Value|.
39scoped_ptr<base::DictionaryValue> ReadValue(const base::win::RegKey& key,
40                                            const wchar_t* value_name) {
41  // presubmit: allow wstring
42  std::wstring value_json;
43  LONG result = key.ReadValue(value_name, &value_json);
44  if (result != ERROR_SUCCESS) {
45    SetLastError(result);
46    PLOG(ERROR) << "Cannot read value '" << value_name << "'";
47    return scoped_ptr<base::DictionaryValue>();
48  }
49
50  // Parse the value.
51  std::string value_json_utf8 = WideToUTF8(value_json);
52  JSONStringValueSerializer serializer(&value_json_utf8);
53  int error_code;
54  std::string error_message;
55  scoped_ptr<base::Value> value(serializer.Deserialize(&error_code,
56                                                       &error_message));
57  if (!value) {
58    LOG(ERROR) << "Failed to parse '" << value_name << "': " << error_message
59               << " (" << error_code << ").";
60    return scoped_ptr<base::DictionaryValue>();
61  }
62
63  if (value->GetType() != base::Value::TYPE_DICTIONARY) {
64    LOG(ERROR) << "Failed to parse '" << value_name << "': not a dictionary.";
65    return scoped_ptr<base::DictionaryValue>();
66  }
67
68  return scoped_ptr<base::DictionaryValue>(
69      static_cast<base::DictionaryValue*>(value.release()));
70}
71
72// Serializes |value| into a JSON string and writes it as value |value_name|
73// under |key|.
74bool WriteValue(base::win::RegKey& key,
75                const wchar_t* value_name,
76                scoped_ptr<base::DictionaryValue> value) {
77  std::string value_json_utf8;
78  JSONStringValueSerializer serializer(&value_json_utf8);
79  if (!serializer.Serialize(*value)) {
80    LOG(ERROR) << "Failed to serialize '" << value_name << "'";
81    return false;
82  }
83
84  // presubmit: allow wstring
85  std::wstring value_json = UTF8ToWide(value_json_utf8);
86  LONG result = key.WriteValue(value_name,
87                               UTF8ToWide(value_json_utf8).c_str());
88  if (result != ERROR_SUCCESS) {
89    SetLastError(result);
90    PLOG(ERROR) << "Cannot write value '" << value_name << "'";
91    return false;
92  }
93
94  return true;
95}
96
97}  // namespace
98
99using protocol::PairingRegistry;
100
101PairingRegistryDelegateWin::PairingRegistryDelegateWin() {
102}
103
104PairingRegistryDelegateWin::~PairingRegistryDelegateWin() {
105}
106
107bool PairingRegistryDelegateWin::SetRootKeys(HKEY privileged,
108                                             HKEY unprivileged) {
109  DCHECK(!privileged_.Valid());
110  DCHECK(!unprivileged_.Valid());
111  DCHECK(unprivileged);
112
113  if (!DuplicateKeyHandle(unprivileged, &unprivileged_))
114    return false;
115
116  if (privileged) {
117    if (!DuplicateKeyHandle(privileged, &privileged_))
118      return false;
119  }
120
121  return true;
122}
123
124scoped_ptr<base::ListValue> PairingRegistryDelegateWin::LoadAll() {
125  scoped_ptr<base::ListValue> pairings(new base::ListValue());
126
127  // Enumerate and parse all values under the unprivileged key.
128  DWORD count = unprivileged_.GetValueCount();
129  for (DWORD index = 0; index < count; ++index) {
130    // presubmit: allow wstring
131    std::wstring value_name;
132    LONG result = unprivileged_.GetValueNameAt(index, &value_name);
133    if (result != ERROR_SUCCESS) {
134      SetLastError(result);
135      PLOG(ERROR) << "Cannot get the name of value " << index;
136      continue;
137    }
138
139    PairingRegistry::Pairing pairing = Load(WideToUTF8(value_name));
140    if (pairing.is_valid())
141      pairings->Append(pairing.ToValue().release());
142  }
143
144  return pairings.Pass();
145}
146
147bool PairingRegistryDelegateWin::DeleteAll() {
148  if (!privileged_.Valid()) {
149    LOG(ERROR) << "Cannot delete pairings: the delegate is read-only.";
150    return false;
151  }
152
153  bool success = true;
154  DWORD count = unprivileged_.GetValueCount();
155  while (count > 0) {
156    // presubmit: allow wstring
157    std::wstring value_name;
158    LONG result = unprivileged_.GetValueNameAt(0, &value_name);
159    if (result == ERROR_SUCCESS)
160      result = unprivileged_.DeleteValue(value_name.c_str());
161
162    success = success && (result == ERROR_SUCCESS);
163    count = unprivileged_.GetValueCount();
164  }
165
166  return success;
167}
168
169PairingRegistry::Pairing PairingRegistryDelegateWin::Load(
170    const std::string& client_id) {
171  // presubmit: allow wstring
172  std::wstring value_name = UTF8ToWide(client_id);
173
174  // Read unprivileged fields first.
175  scoped_ptr<base::DictionaryValue> pairing = ReadValue(unprivileged_,
176                                                        value_name.c_str());
177  if (!pairing)
178    return PairingRegistry::Pairing();
179
180  // Read the shared secret.
181  if (privileged_.Valid()) {
182    scoped_ptr<base::DictionaryValue> secret = ReadValue(privileged_,
183                                                         value_name.c_str());
184    if (!secret)
185      return PairingRegistry::Pairing();
186
187    // Merge the two dictionaries.
188    pairing->MergeDictionary(secret.get());
189  }
190
191  return PairingRegistry::Pairing::CreateFromValue(*pairing);
192}
193
194bool PairingRegistryDelegateWin::Save(const PairingRegistry::Pairing& pairing) {
195  if (!privileged_.Valid()) {
196    LOG(ERROR) << "Cannot save pairing entry '" << pairing.client_id()
197                << "': the delegate is read-only.";
198    return false;
199  }
200
201  // Convert pairing to JSON.
202  scoped_ptr<base::DictionaryValue> pairing_json = pairing.ToValue();
203
204  // Extract the shared secret to a separate dictionary.
205  scoped_ptr<base::Value> secret_key;
206  CHECK(pairing_json->Remove(PairingRegistry::kSharedSecretKey, &secret_key));
207  scoped_ptr<base::DictionaryValue> secret_json(new base::DictionaryValue());
208  secret_json->Set(PairingRegistry::kSharedSecretKey, secret_key.release());
209
210  // presubmit: allow wstring
211  std::wstring value_name = UTF8ToWide(pairing.client_id());
212
213  // Write pairing to the registry.
214  if (!WriteValue(privileged_, value_name.c_str(), secret_json.Pass()) ||
215      !WriteValue(unprivileged_, value_name.c_str(), pairing_json.Pass())) {
216    return false;
217  }
218
219  return true;
220}
221
222bool PairingRegistryDelegateWin::Delete(const std::string& client_id) {
223  if (!privileged_.Valid()) {
224    LOG(ERROR) << "Cannot delete pairing entry '" << client_id
225                << "': the delegate is read-only.";
226    return false;
227  }
228
229  // presubmit: allow wstring
230  std::wstring value_name = UTF8ToWide(client_id);
231  LONG result = privileged_.DeleteValue(value_name.c_str());
232  if (result != ERROR_SUCCESS &&
233      result != ERROR_FILE_NOT_FOUND &&
234      result != ERROR_PATH_NOT_FOUND) {
235    SetLastError(result);
236    PLOG(ERROR) << "Cannot delete pairing entry '" << client_id << "'";
237    return false;
238  }
239
240  result = unprivileged_.DeleteValue(value_name.c_str());
241  if (result != ERROR_SUCCESS &&
242      result != ERROR_FILE_NOT_FOUND &&
243      result != ERROR_PATH_NOT_FOUND) {
244    SetLastError(result);
245    PLOG(ERROR) << "Cannot delete pairing entry '" << client_id << "'";
246    return false;
247  }
248
249  return true;
250}
251
252scoped_ptr<PairingRegistry::Delegate> CreatePairingRegistryDelegate() {
253  return scoped_ptr<PairingRegistry::Delegate>();
254}
255
256}  // namespace remoting
257