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