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