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/protocol/pairing_registry.h"
6
7#include "base/base64.h"
8#include "base/bind.h"
9#include "base/guid.h"
10#include "base/json/json_string_value_serializer.h"
11#include "base/location.h"
12#include "base/single_thread_task_runner.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/thread_task_runner_handle.h"
15#include "base/values.h"
16#include "crypto/random.h"
17
18namespace remoting {
19namespace protocol {
20
21// How many bytes of random data to use for the shared secret.
22const int kKeySize = 16;
23
24const char PairingRegistry::kCreatedTimeKey[] = "createdTime";
25const char PairingRegistry::kClientIdKey[] = "clientId";
26const char PairingRegistry::kClientNameKey[] = "clientName";
27const char PairingRegistry::kSharedSecretKey[] = "sharedSecret";
28
29PairingRegistry::Pairing::Pairing() {
30}
31
32PairingRegistry::Pairing::Pairing(const base::Time& created_time,
33                                  const std::string& client_name,
34                                  const std::string& client_id,
35                                  const std::string& shared_secret)
36    : created_time_(created_time),
37      client_name_(client_name),
38      client_id_(client_id),
39      shared_secret_(shared_secret) {
40}
41
42PairingRegistry::Pairing::~Pairing() {
43}
44
45PairingRegistry::Pairing PairingRegistry::Pairing::Create(
46    const std::string& client_name) {
47  base::Time created_time = base::Time::Now();
48  std::string client_id = base::GenerateGUID();
49  std::string shared_secret;
50  char buffer[kKeySize];
51  crypto::RandBytes(buffer, arraysize(buffer));
52  base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)),
53                     &shared_secret);
54  return Pairing(created_time, client_name, client_id, shared_secret);
55}
56
57PairingRegistry::Pairing PairingRegistry::Pairing::CreateFromValue(
58    const base::DictionaryValue& pairing) {
59  std::string client_name, client_id;
60  double created_time_value;
61  if (pairing.GetDouble(kCreatedTimeKey, &created_time_value) &&
62      pairing.GetString(kClientNameKey, &client_name) &&
63      pairing.GetString(kClientIdKey, &client_id)) {
64    // The shared secret is optional.
65    std::string shared_secret;
66    pairing.GetString(kSharedSecretKey, &shared_secret);
67    base::Time created_time = base::Time::FromJsTime(created_time_value);
68    return Pairing(created_time, client_name, client_id, shared_secret);
69  }
70
71  LOG(ERROR) << "Failed to load pairing information: unexpected format.";
72  return Pairing();
73}
74
75scoped_ptr<base::DictionaryValue> PairingRegistry::Pairing::ToValue() const {
76  scoped_ptr<base::DictionaryValue> pairing(new base::DictionaryValue());
77  pairing->SetDouble(kCreatedTimeKey, created_time().ToJsTime());
78  pairing->SetString(kClientNameKey, client_name());
79  pairing->SetString(kClientIdKey, client_id());
80  if (!shared_secret().empty())
81    pairing->SetString(kSharedSecretKey, shared_secret());
82  return pairing.Pass();
83}
84
85bool PairingRegistry::Pairing::operator==(const Pairing& other) const {
86  return created_time_ == other.created_time_ &&
87         client_id_ == other.client_id_ &&
88         client_name_ == other.client_name_ &&
89         shared_secret_ == other.shared_secret_;
90}
91
92bool PairingRegistry::Pairing::is_valid() const {
93  // |shared_secret_| is optional. It will be empty on Windows because the
94  // privileged registry key can only be read in the elevated host process.
95  return !client_id_.empty();
96}
97
98PairingRegistry::PairingRegistry(
99    scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner,
100    scoped_ptr<Delegate> delegate)
101    : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
102      delegate_task_runner_(delegate_task_runner),
103      delegate_(delegate.Pass()) {
104  DCHECK(delegate_);
105}
106
107PairingRegistry::Pairing PairingRegistry::CreatePairing(
108    const std::string& client_name) {
109  DCHECK(caller_task_runner_->BelongsToCurrentThread());
110
111  Pairing result = Pairing::Create(client_name);
112  AddPairing(result);
113  return result;
114}
115
116void PairingRegistry::GetPairing(const std::string& client_id,
117                                 const GetPairingCallback& callback) {
118  DCHECK(caller_task_runner_->BelongsToCurrentThread());
119
120  GetPairingCallback wrapped_callback = base::Bind(
121      &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext,
122      this, callback);
123  base::Closure request = base::Bind(
124      &PairingRegistry::DoLoad, this, client_id, wrapped_callback);
125  ServiceOrQueueRequest(request);
126}
127
128void PairingRegistry::GetAllPairings(
129    const GetAllPairingsCallback& callback) {
130  DCHECK(caller_task_runner_->BelongsToCurrentThread());
131
132  GetAllPairingsCallback wrapped_callback = base::Bind(
133      &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext,
134      this, callback);
135  GetAllPairingsCallback sanitize_callback = base::Bind(
136      &PairingRegistry::SanitizePairings,
137      this, wrapped_callback);
138  base::Closure request = base::Bind(
139      &PairingRegistry::DoLoadAll, this, sanitize_callback);
140  ServiceOrQueueRequest(request);
141}
142
143void PairingRegistry::DeletePairing(
144    const std::string& client_id, const DoneCallback& callback) {
145  DCHECK(caller_task_runner_->BelongsToCurrentThread());
146
147  DoneCallback wrapped_callback = base::Bind(
148      &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
149      this, callback);
150  base::Closure request = base::Bind(
151      &PairingRegistry::DoDelete, this, client_id, wrapped_callback);
152  ServiceOrQueueRequest(request);
153}
154
155void PairingRegistry::ClearAllPairings(
156    const DoneCallback& callback) {
157  DCHECK(caller_task_runner_->BelongsToCurrentThread());
158
159  DoneCallback wrapped_callback = base::Bind(
160      &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
161      this, callback);
162  base::Closure request = base::Bind(
163      &PairingRegistry::DoDeleteAll, this, wrapped_callback);
164  ServiceOrQueueRequest(request);
165}
166
167PairingRegistry::~PairingRegistry() {
168}
169
170void PairingRegistry::PostTask(
171    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
172    const tracked_objects::Location& from_here,
173    const base::Closure& task) {
174  task_runner->PostTask(from_here, task);
175}
176
177void PairingRegistry::AddPairing(const Pairing& pairing) {
178  DoneCallback wrapped_callback = base::Bind(
179      &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
180      this, DoneCallback());
181  base::Closure request = base::Bind(
182      &PairingRegistry::DoSave, this, pairing, wrapped_callback);
183  ServiceOrQueueRequest(request);
184}
185
186void PairingRegistry::DoLoadAll(
187    const protocol::PairingRegistry::GetAllPairingsCallback& callback) {
188  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
189
190  scoped_ptr<base::ListValue> pairings = delegate_->LoadAll();
191  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback,
192                                                      base::Passed(&pairings)));
193}
194
195void PairingRegistry::DoDeleteAll(
196    const protocol::PairingRegistry::DoneCallback& callback) {
197  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
198
199  bool success = delegate_->DeleteAll();
200  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
201}
202
203void PairingRegistry::DoLoad(
204    const std::string& client_id,
205    const protocol::PairingRegistry::GetPairingCallback& callback) {
206  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
207
208  Pairing pairing = delegate_->Load(client_id);
209  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, pairing));
210}
211
212void PairingRegistry::DoSave(
213    const protocol::PairingRegistry::Pairing& pairing,
214    const protocol::PairingRegistry::DoneCallback& callback) {
215  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
216
217  bool success = delegate_->Save(pairing);
218  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
219}
220
221void PairingRegistry::DoDelete(
222    const std::string& client_id,
223    const protocol::PairingRegistry::DoneCallback& callback) {
224  DCHECK(delegate_task_runner_->BelongsToCurrentThread());
225
226  bool success = delegate_->Delete(client_id);
227  PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
228}
229
230void PairingRegistry::InvokeDoneCallbackAndScheduleNext(
231    const DoneCallback& callback, bool success) {
232  // CreatePairing doesn't have a callback, so the callback can be null.
233  if (!callback.is_null())
234    callback.Run(success);
235
236  pending_requests_.pop();
237  ServiceNextRequest();
238}
239
240void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext(
241    const GetPairingCallback& callback, Pairing pairing) {
242  callback.Run(pairing);
243  pending_requests_.pop();
244  ServiceNextRequest();
245}
246
247void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext(
248    const GetAllPairingsCallback& callback,
249    scoped_ptr<base::ListValue> pairings) {
250  callback.Run(pairings.Pass());
251  pending_requests_.pop();
252  ServiceNextRequest();
253}
254
255void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback,
256                                       scoped_ptr<base::ListValue> pairings) {
257  DCHECK(caller_task_runner_->BelongsToCurrentThread());
258
259  scoped_ptr<base::ListValue> sanitized_pairings(new base::ListValue());
260  for (size_t i = 0; i < pairings->GetSize(); ++i) {
261    base::DictionaryValue* pairing_json;
262    if (!pairings->GetDictionary(i, &pairing_json)) {
263      LOG(WARNING) << "A pairing entry is not a dictionary.";
264      continue;
265    }
266
267    // Parse the pairing data.
268    Pairing pairing = Pairing::CreateFromValue(*pairing_json);
269    if (!pairing.is_valid()) {
270      LOG(WARNING) << "Could not parse a pairing entry.";
271      continue;
272    }
273
274    // Clear the shared secrect and append the pairing data to the list.
275    Pairing sanitized_pairing(
276        pairing.created_time(),
277        pairing.client_name(),
278        pairing.client_id(),
279        "");
280    sanitized_pairings->Append(sanitized_pairing.ToValue().release());
281  }
282
283  callback.Run(sanitized_pairings.Pass());
284}
285
286void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) {
287  bool servicing_request = !pending_requests_.empty();
288  pending_requests_.push(request);
289  if (!servicing_request) {
290    ServiceNextRequest();
291  }
292}
293
294void PairingRegistry::ServiceNextRequest() {
295  if (pending_requests_.empty())
296    return;
297
298  PostTask(delegate_task_runner_, FROM_HERE, pending_requests_.front());
299}
300
301}  // namespace protocol
302}  // namespace remoting
303