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_authenticator_base.h"
      6 
      7 #include "base/bind.h"
      8 #include "remoting/base/constants.h"
      9 #include "remoting/protocol/channel_authenticator.h"
     10 
     11 namespace remoting {
     12 namespace protocol {
     13 
     14 const buzz::StaticQName PairingAuthenticatorBase::kPairingInfoTag =
     15     { kChromotingXmlNamespace, "pairing-info" };
     16 const buzz::StaticQName PairingAuthenticatorBase::kClientIdAttribute =
     17     { "", "client-id" };
     18 
     19 namespace {
     20 const buzz::StaticQName kPairingFailedTag =
     21     { kChromotingXmlNamespace, "pairing-failed" };
     22 const buzz::StaticQName kPairingErrorAttribute = { "", "error" };
     23 }  // namespace
     24 
     25 PairingAuthenticatorBase::PairingAuthenticatorBase()
     26     : using_paired_secret_(false),
     27       waiting_for_authenticator_(false),
     28       weak_factory_(this) {
     29 }
     30 
     31 PairingAuthenticatorBase::~PairingAuthenticatorBase() {
     32 }
     33 
     34 Authenticator::State PairingAuthenticatorBase::state() const {
     35   if (waiting_for_authenticator_) {
     36     return PROCESSING_MESSAGE;
     37   }
     38   return v2_authenticator_->state();
     39 }
     40 
     41 bool PairingAuthenticatorBase::started() const {
     42   if (!v2_authenticator_) {
     43     return false;
     44   }
     45   return v2_authenticator_->started();
     46 }
     47 
     48 Authenticator::RejectionReason
     49 PairingAuthenticatorBase::rejection_reason() const {
     50   if (!v2_authenticator_) {
     51     return PROTOCOL_ERROR;
     52   }
     53   return v2_authenticator_->rejection_reason();
     54 }
     55 
     56 void PairingAuthenticatorBase::ProcessMessage(
     57     const buzz::XmlElement* message,
     58     const base::Closure& resume_callback) {
     59   DCHECK_EQ(state(), WAITING_MESSAGE);
     60 
     61   // The client authenticator creates the underlying authenticator in the ctor
     62   // and the host creates it in response to the first message before deferring
     63   // to this class to process it. Either way, it should exist here.
     64   DCHECK(v2_authenticator_);
     65 
     66   // If pairing failed, and we haven't already done so, try again with the PIN.
     67   if (using_paired_secret_ && HasErrorMessage(message)) {
     68     using_paired_secret_ = false;
     69     waiting_for_authenticator_ = true;
     70     v2_authenticator_.reset();
     71     SetAuthenticatorCallback set_authenticator = base::Bind(
     72         &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage,
     73         weak_factory_.GetWeakPtr(), base::Owned(new buzz::XmlElement(*message)),
     74         resume_callback);
     75     CreateV2AuthenticatorWithPIN(WAITING_MESSAGE, set_authenticator);
     76     return;
     77   }
     78 
     79   // Pass the message to the underlying authenticator for processing, but
     80   // check for a failed SPAKE exchange if we're using the paired secret. In
     81   // this case the pairing protocol can continue by communicating the error
     82   // to the peer and retrying with the PIN.
     83   v2_authenticator_->ProcessMessage(
     84       message,
     85       base::Bind(&PairingAuthenticatorBase::CheckForFailedSpakeExchange,
     86                  weak_factory_.GetWeakPtr(), resume_callback));
     87 }
     88 
     89 scoped_ptr<buzz::XmlElement> PairingAuthenticatorBase::GetNextMessage() {
     90   DCHECK_EQ(state(), MESSAGE_READY);
     91   scoped_ptr<buzz::XmlElement> result = v2_authenticator_->GetNextMessage();
     92   AddPairingElements(result.get());
     93   MaybeAddErrorMessage(result.get());
     94   return result.Pass();
     95 }
     96 
     97 scoped_ptr<ChannelAuthenticator>
     98 PairingAuthenticatorBase::CreateChannelAuthenticator() const {
     99   return v2_authenticator_->CreateChannelAuthenticator();
    100 }
    101 
    102 void PairingAuthenticatorBase::MaybeAddErrorMessage(buzz::XmlElement* message) {
    103   if (!error_message_.empty()) {
    104     buzz::XmlElement* pairing_failed_tag =
    105         new buzz::XmlElement(kPairingFailedTag);
    106     pairing_failed_tag->AddAttr(kPairingErrorAttribute, error_message_);
    107     message->AddElement(pairing_failed_tag);
    108     error_message_.clear();
    109   }
    110 }
    111 
    112 bool PairingAuthenticatorBase::HasErrorMessage(
    113     const buzz::XmlElement* message) const {
    114   const buzz::XmlElement* pairing_failed_tag =
    115       message->FirstNamed(kPairingFailedTag);
    116   if (pairing_failed_tag) {
    117     std::string error = pairing_failed_tag->Attr(kPairingErrorAttribute);
    118     LOG(ERROR) << "Pairing failed: " << error;
    119   }
    120   return pairing_failed_tag != NULL;
    121 }
    122 
    123 void PairingAuthenticatorBase::CheckForFailedSpakeExchange(
    124     const base::Closure& resume_callback) {
    125   // If the SPAKE exchange failed due to invalid credentials, and those
    126   // credentials were the paired secret, then notify the peer that the
    127   // PIN-less connection failed and retry using the PIN.
    128   if (v2_authenticator_->state() == REJECTED &&
    129       v2_authenticator_->rejection_reason() == INVALID_CREDENTIALS &&
    130       using_paired_secret_) {
    131     using_paired_secret_ = false;
    132     error_message_ = "invalid-shared-secret";
    133     v2_authenticator_.reset();
    134     buzz::XmlElement* no_message = NULL;
    135     SetAuthenticatorCallback set_authenticator = base::Bind(
    136         &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage,
    137         weak_factory_.GetWeakPtr(), no_message, resume_callback);
    138     CreateV2AuthenticatorWithPIN(MESSAGE_READY, set_authenticator);
    139     return;
    140   }
    141 
    142   resume_callback.Run();
    143 }
    144 
    145 void PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage(
    146     const buzz::XmlElement* message,
    147     const base::Closure& resume_callback,
    148     scoped_ptr<Authenticator> authenticator) {
    149   DCHECK(!v2_authenticator_);
    150   DCHECK(authenticator);
    151   waiting_for_authenticator_ = false;
    152   v2_authenticator_ = authenticator.Pass();
    153   if (message) {
    154     ProcessMessage(message, resume_callback);
    155   } else {
    156     resume_callback.Run();
    157   }
    158 }
    159 
    160 }  // namespace protocol
    161 }  // namespace remoting
    162