• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 namespace remoting {
19 namespace protocol {
20 
21 // How many bytes of random data to use for the shared secret.
22 const int kKeySize = 16;
23 
24 const char PairingRegistry::kCreatedTimeKey[] = "createdTime";
25 const char PairingRegistry::kClientIdKey[] = "clientId";
26 const char PairingRegistry::kClientNameKey[] = "clientName";
27 const char PairingRegistry::kSharedSecretKey[] = "sharedSecret";
28 
Pairing()29 PairingRegistry::Pairing::Pairing() {
30 }
31 
Pairing(const base::Time & created_time,const std::string & client_name,const std::string & client_id,const std::string & shared_secret)32 PairingRegistry::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 
~Pairing()42 PairingRegistry::Pairing::~Pairing() {
43 }
44 
Create(const std::string & client_name)45 PairingRegistry::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 
CreateFromValue(const base::DictionaryValue & pairing)57 PairingRegistry::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 
ToValue() const75 scoped_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 
operator ==(const Pairing & other) const85 bool 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 
is_valid() const92 bool 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 
PairingRegistry(scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner,scoped_ptr<Delegate> delegate)98 PairingRegistry::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 
CreatePairing(const std::string & client_name)107 PairingRegistry::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 
GetPairing(const std::string & client_id,const GetPairingCallback & callback)116 void 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 
GetAllPairings(const GetAllPairingsCallback & callback)128 void 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 
DeletePairing(const std::string & client_id,const DoneCallback & callback)143 void 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 
ClearAllPairings(const DoneCallback & callback)155 void 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 
~PairingRegistry()167 PairingRegistry::~PairingRegistry() {
168 }
169 
PostTask(const scoped_refptr<base::SingleThreadTaskRunner> & task_runner,const tracked_objects::Location & from_here,const base::Closure & task)170 void 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 
AddPairing(const Pairing & pairing)177 void 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 
DoLoadAll(const protocol::PairingRegistry::GetAllPairingsCallback & callback)186 void 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 
DoDeleteAll(const protocol::PairingRegistry::DoneCallback & callback)195 void 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 
DoLoad(const std::string & client_id,const protocol::PairingRegistry::GetPairingCallback & callback)203 void 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 
DoSave(const protocol::PairingRegistry::Pairing & pairing,const protocol::PairingRegistry::DoneCallback & callback)212 void 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 
DoDelete(const std::string & client_id,const protocol::PairingRegistry::DoneCallback & callback)221 void 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 
InvokeDoneCallbackAndScheduleNext(const DoneCallback & callback,bool success)230 void 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 
InvokeGetPairingCallbackAndScheduleNext(const GetPairingCallback & callback,Pairing pairing)240 void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext(
241     const GetPairingCallback& callback, Pairing pairing) {
242   callback.Run(pairing);
243   pending_requests_.pop();
244   ServiceNextRequest();
245 }
246 
InvokeGetAllPairingsCallbackAndScheduleNext(const GetAllPairingsCallback & callback,scoped_ptr<base::ListValue> pairings)247 void 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 
SanitizePairings(const GetAllPairingsCallback & callback,scoped_ptr<base::ListValue> pairings)255 void 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 
ServiceOrQueueRequest(const base::Closure & request)286 void 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 
ServiceNextRequest()294 void 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