| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/protocol/negotiating_host_authenticator.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <sstream> |
| #include <utility> |
| |
| #include "base/check_op.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/notreached.h" |
| #include "base/strings/string_split.h" |
| #include "remoting/base/rsa_key_pair.h" |
| #include "remoting/protocol/credentials_type.h" |
| #include "remoting/protocol/host_authentication_config.h" |
| #include "remoting/protocol/pairing_host_authenticator.h" |
| #include "remoting/protocol/pairing_registry.h" |
| #include "remoting/protocol/session_authz_authenticator.h" |
| #include "remoting/protocol/spake2_authenticator.h" |
| |
| namespace remoting::protocol { |
| |
| NegotiatingHostAuthenticator::NegotiatingHostAuthenticator( |
| std::string_view local_id, |
| std::string_view remote_id, |
| std::unique_ptr<HostAuthenticationConfig> config) |
| : NegotiatingAuthenticatorBase(WAITING_MESSAGE), |
| local_id_(local_id), |
| remote_id_(remote_id), |
| config_(std::move(config)) { |
| methods_ = config_->GetSupportedMethods(); |
| DCHECK(!methods_.empty()); |
| } |
| |
| NegotiatingHostAuthenticator::~NegotiatingHostAuthenticator() = default; |
| |
| void NegotiatingHostAuthenticator::ProcessMessage( |
| const JingleAuthentication& message, |
| base::OnceClosure resume_callback) { |
| DCHECK_EQ(state(), WAITING_MESSAGE); |
| state_ = PROCESSING_MESSAGE; |
| |
| if (message.pairing_info) { |
| client_id_ = message.pairing_info->client_id; |
| } |
| |
| AuthenticationMethod method = |
| message.method.value_or(AuthenticationMethod::INVALID); |
| |
| if (current_method_ != AuthenticationMethod::INVALID && |
| method != current_method_) { |
| state_ = REJECTED; |
| rejection_reason_ = RejectionReason::INVALID_STATE; |
| rejection_details_ = RejectionDetails( |
| "The host has already chosen an authentication method. " |
| "The client cannot change it."); |
| std::move(resume_callback).Run(); |
| return; |
| } |
| |
| // If the client did not specify a preferred auth method, or specified an |
| // unknown or unsupported method, then select the first known method from |
| // the supported-methods attribute. |
| if (method == AuthenticationMethod::INVALID || |
| !std::ranges::contains(methods_, method)) { |
| method = AuthenticationMethod::INVALID; |
| |
| if (message.supported_methods.empty()) { |
| state_ = REJECTED; |
| rejection_reason_ = RejectionReason::INVALID_ARGUMENT; |
| rejection_details_ = RejectionDetails( |
| "Message contains neither the 'method' nor the 'supported-methods' " |
| "attributes."); |
| std::move(resume_callback).Run(); |
| return; |
| } |
| |
| // Find the first mutually-supported method in the client's list of |
| // supported-methods. |
| for (AuthenticationMethod value : message.supported_methods) { |
| if (std::ranges::contains(methods_, value)) { |
| // Found common method. |
| method = value; |
| break; |
| } |
| } |
| |
| if (method == AuthenticationMethod::INVALID) { |
| state_ = REJECTED; |
| rejection_reason_ = RejectionReason::NO_COMMON_AUTH_METHOD; |
| rejection_details_ = RejectionDetails( |
| "No common authentication method found between client and host."); |
| std::move(resume_callback).Run(); |
| return; |
| } |
| |
| // Drop the current message because we've chosen a different method. |
| current_method_ = method; |
| CreateAuthenticator( |
| MESSAGE_READY, |
| base::BindOnce(&NegotiatingHostAuthenticator::UpdateState, |
| weak_factory_.GetWeakPtr(), std::move(resume_callback))); |
| return; |
| } |
| |
| // If the client specified a supported method, and the host hasn't chosen a |
| // method yet, use the client's preferred method and process the message. |
| if (current_method_ == AuthenticationMethod::INVALID) { |
| current_method_ = method; |
| // Copy the message since the authenticator may process it asynchronously. |
| CreateAuthenticator( |
| WAITING_MESSAGE, |
| base::BindOnce(&NegotiatingAuthenticatorBase::ProcessMessageInternal, |
| weak_factory_.GetWeakPtr(), message, |
| std::move(resume_callback))); |
| return; |
| } |
| |
| // If the client is using the host's current method, just process the message. |
| ProcessMessageInternal(message, std::move(resume_callback)); |
| } |
| |
| JingleAuthentication NegotiatingHostAuthenticator::GetNextMessage() { |
| return GetNextMessageInternal(); |
| } |
| |
| void NegotiatingHostAuthenticator::CreateAuthenticator( |
| Authenticator::State preferred_initial_state, |
| base::OnceClosure resume_callback) { |
| DCHECK(current_method_ != AuthenticationMethod::INVALID); |
| |
| auto weak_this = weak_factory_.GetWeakPtr(); |
| |
| switch (current_method_) { |
| case AuthenticationMethod::INVALID: |
| NOTREACHED(); |
| |
| case AuthenticationMethod::CLOUD_SESSION_AUTHZ_SPAKE2_CURVE25519: { |
| DCHECK_EQ(config_->session_authz_client_factory->method(), |
| AuthenticationMethod::CLOUD_SESSION_AUTHZ_SPAKE2_CURVE25519); |
| auto authenticator = std::make_unique<SessionAuthzAuthenticator>( |
| CredentialsType::CLOUD_SESSION_AUTHZ, |
| config_->session_authz_client_factory->Create(), |
| base::BindRepeating(&Spake2Authenticator::CreateForHost, local_id_, |
| remote_id_, config_->local_cert, |
| config_->key_pair)); |
| SessionAuthzAuthenticator* auth_ptr = authenticator.get(); |
| current_authenticator_ = std::move(authenticator); |
| auth_ptr->Start(std::move(resume_callback)); |
| break; |
| } |
| |
| case AuthenticationMethod::CORP_SESSION_AUTHZ_SPAKE2_CURVE25519: { |
| DCHECK_EQ(config_->session_authz_client_factory->method(), |
| AuthenticationMethod::CORP_SESSION_AUTHZ_SPAKE2_CURVE25519); |
| auto authenticator = std::make_unique<SessionAuthzAuthenticator>( |
| CredentialsType::CORP_SESSION_AUTHZ, |
| config_->session_authz_client_factory->Create(), |
| base::BindRepeating(&Spake2Authenticator::CreateForHost, local_id_, |
| remote_id_, config_->local_cert, |
| config_->key_pair)); |
| SessionAuthzAuthenticator* auth_ptr = authenticator.get(); |
| current_authenticator_ = std::move(authenticator); |
| auth_ptr->Start(std::move(resume_callback)); |
| break; |
| } |
| |
| case AuthenticationMethod::PAIRED_SPAKE2_CURVE25519: { |
| auto pairing_authenticator = std::make_unique<PairingHostAuthenticator>( |
| config_->pairing_registry, |
| base::BindRepeating(&Spake2Authenticator::CreateForHost, local_id_, |
| remote_id_, config_->local_cert, |
| config_->key_pair), |
| config_->shared_secret_hash); |
| PairingHostAuthenticator* auth_ptr = pairing_authenticator.get(); |
| current_authenticator_ = std::move(pairing_authenticator); |
| auth_ptr->Initialize(client_id_, preferred_initial_state, |
| std::move(resume_callback)); |
| break; |
| } |
| |
| case AuthenticationMethod::SHARED_SECRET_SPAKE2_CURVE25519: |
| current_authenticator_ = Spake2Authenticator::CreateForHost( |
| local_id_, remote_id_, config_->local_cert, config_->key_pair, |
| config_->shared_secret_hash, preferred_initial_state); |
| std::move(resume_callback).Run(); |
| break; |
| } |
| |
| if (!weak_this) { |
| return; |
| } |
| |
| ChainStateChangeAfterAcceptedWithUnderlying(*current_authenticator_); |
| } |
| |
| } // namespace remoting::protocol |