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 11namespace remoting { 12namespace protocol { 13 14const buzz::StaticQName PairingAuthenticatorBase::kPairingInfoTag = 15 { kChromotingXmlNamespace, "pairing-info" }; 16const buzz::StaticQName PairingAuthenticatorBase::kClientIdAttribute = 17 { "", "client-id" }; 18 19namespace { 20const buzz::StaticQName kPairingFailedTag = 21 { kChromotingXmlNamespace, "pairing-failed" }; 22const buzz::StaticQName kPairingErrorAttribute = { "", "error" }; 23} // namespace 24 25PairingAuthenticatorBase::PairingAuthenticatorBase() 26 : using_paired_secret_(false), 27 waiting_for_authenticator_(false), 28 weak_factory_(this) { 29} 30 31PairingAuthenticatorBase::~PairingAuthenticatorBase() { 32} 33 34Authenticator::State PairingAuthenticatorBase::state() const { 35 if (waiting_for_authenticator_) { 36 return PROCESSING_MESSAGE; 37 } 38 return v2_authenticator_->state(); 39} 40 41Authenticator::RejectionReason 42PairingAuthenticatorBase::rejection_reason() const { 43 if (!v2_authenticator_) { 44 return PROTOCOL_ERROR; 45 } 46 return v2_authenticator_->rejection_reason(); 47} 48 49void 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 82scoped_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 90scoped_ptr<ChannelAuthenticator> 91PairingAuthenticatorBase::CreateChannelAuthenticator() const { 92 return v2_authenticator_->CreateChannelAuthenticator(); 93} 94 95void 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 105bool 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(ERROR) << "Pairing failed: " << error; 112 } 113 return pairing_failed_tag != NULL; 114} 115 116void 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 138void 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