Home | History | Annotate | Download | only in protocol
      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