// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/protocol/pairing_authenticator_base.h" #include #include "base/bind.h" #include "base/callback.h" #include "base/logging.h" #include "remoting/base/constants.h" #include "remoting/protocol/channel_authenticator.h" namespace remoting { namespace protocol { namespace { const jingle_xmpp::StaticQName kPairingFailedTag = { kChromotingXmlNamespace, "pairing-failed" }; const jingle_xmpp::StaticQName kPairingErrorAttribute = { "", "error" }; } // namespace PairingAuthenticatorBase::PairingAuthenticatorBase() {} PairingAuthenticatorBase::~PairingAuthenticatorBase() = default; Authenticator::State PairingAuthenticatorBase::state() const { DCHECK(spake2_authenticator_); return spake2_authenticator_->state(); } bool PairingAuthenticatorBase::started() const { if (!spake2_authenticator_) { return false; } return spake2_authenticator_->started(); } Authenticator::RejectionReason PairingAuthenticatorBase::rejection_reason() const { if (!spake2_authenticator_) { return RejectionReason::PROTOCOL_ERROR; } return spake2_authenticator_->rejection_reason(); } void PairingAuthenticatorBase::ProcessMessage( const jingle_xmpp::XmlElement* message, base::OnceClosure resume_callback) { DCHECK_EQ(state(), WAITING_MESSAGE); // The client authenticator creates the underlying authenticator in the ctor // and the host creates it in response to the first message before deferring // to this class to process it. Either way, it should exist here. DCHECK(spake2_authenticator_); // If pairing failed, and we haven't already done so, try again with the PIN. if (using_paired_secret_ && HasErrorMessage(message)) { using_paired_secret_ = false; spake2_authenticator_.reset(); CreateSpakeAuthenticatorWithPin( WAITING_MESSAGE, base::BindOnce(&PairingAuthenticatorBase::ProcessMessage, weak_factory_.GetWeakPtr(), base::Owned(new jingle_xmpp::XmlElement(*message)), std::move(resume_callback))); return; } // Pass the message to the underlying authenticator for processing, but // check for a failed SPAKE exchange if we're using the paired secret. In // this case the pairing protocol can continue by communicating the error // to the peer and retrying with the PIN. spake2_authenticator_->ProcessMessage( message, base::BindOnce(&PairingAuthenticatorBase::CheckForFailedSpakeExchange, weak_factory_.GetWeakPtr(), std::move(resume_callback))); } std::unique_ptr PairingAuthenticatorBase::GetNextMessage() { DCHECK_EQ(state(), MESSAGE_READY); std::unique_ptr result = spake2_authenticator_->GetNextMessage(); MaybeAddErrorMessage(result.get()); return result; } const std::string& PairingAuthenticatorBase::GetAuthKey() const { return spake2_authenticator_->GetAuthKey(); } std::unique_ptr PairingAuthenticatorBase::CreateChannelAuthenticator() const { return spake2_authenticator_->CreateChannelAuthenticator(); } void PairingAuthenticatorBase::MaybeAddErrorMessage(jingle_xmpp::XmlElement* message) { if (!error_message_.empty()) { jingle_xmpp::XmlElement* pairing_failed_tag = new jingle_xmpp::XmlElement(kPairingFailedTag); pairing_failed_tag->AddAttr(kPairingErrorAttribute, error_message_); message->AddElement(pairing_failed_tag); error_message_.clear(); } } bool PairingAuthenticatorBase::HasErrorMessage( const jingle_xmpp::XmlElement* message) const { const jingle_xmpp::XmlElement* pairing_failed_tag = message->FirstNamed(kPairingFailedTag); if (pairing_failed_tag) { std::string error = pairing_failed_tag->Attr(kPairingErrorAttribute); LOG(ERROR) << "Pairing failed: " << error; } return pairing_failed_tag != nullptr; } void PairingAuthenticatorBase::CheckForFailedSpakeExchange( base::OnceClosure resume_callback) { // If the SPAKE exchange failed due to invalid credentials, and those // credentials were the paired secret, then notify the peer that the // PIN-less connection failed and retry using the PIN. if (spake2_authenticator_->state() == REJECTED && spake2_authenticator_->rejection_reason() == RejectionReason::INVALID_CREDENTIALS && using_paired_secret_) { using_paired_secret_ = false; error_message_ = "invalid-shared-secret"; spake2_authenticator_.reset(); CreateSpakeAuthenticatorWithPin(MESSAGE_READY, std::move(resume_callback)); return; } std::move(resume_callback).Run(); } } // namespace protocol } // namespace remoting