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