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 29 PairingRegistry::Pairing::Pairing() { 30 } 31 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 42 PairingRegistry::Pairing::~Pairing() { 43 } 44 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 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 75 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 85 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 92 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 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 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 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 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 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 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 167 PairingRegistry::~PairingRegistry() { 168 } 169 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 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 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 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 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 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 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 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 240 void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext( 241 const GetPairingCallback& callback, Pairing pairing) { 242 callback.Run(pairing); 243 pending_requests_.pop(); 244 ServiceNextRequest(); 245 } 246 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 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 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 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