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.
5 #include "remoting/protocol/negotiating_client_authenticator.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/logging.h"
13 #include "base/strings/string_split.h"
14 #include "remoting/protocol/channel_authenticator.h"
15 #include "remoting/protocol/pairing_client_authenticator.h"
16 #include "remoting/protocol/v2_authenticator.h"
17 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
22 NegotiatingClientAuthenticator::NegotiatingClientAuthenticator(
23 const std::string
& client_pairing_id
,
24 const std::string
& shared_secret
,
25 const std::string
& authentication_tag
,
26 const FetchSecretCallback
& fetch_secret_callback
,
27 scoped_ptr
<ThirdPartyClientAuthenticator::TokenFetcher
> token_fetcher
,
28 const std::vector
<AuthenticationMethod
>& methods
)
29 : NegotiatingAuthenticatorBase(MESSAGE_READY
),
30 client_pairing_id_(client_pairing_id
),
31 shared_secret_(shared_secret
),
32 authentication_tag_(authentication_tag
),
33 fetch_secret_callback_(fetch_secret_callback
),
34 token_fetcher_(token_fetcher
.Pass()),
35 method_set_by_host_(false),
37 DCHECK(!methods
.empty());
38 for (std::vector
<AuthenticationMethod
>::const_iterator it
= methods
.begin();
39 it
!= methods
.end(); ++it
) {
44 NegotiatingClientAuthenticator::~NegotiatingClientAuthenticator() {
47 void NegotiatingClientAuthenticator::ProcessMessage(
48 const buzz::XmlElement
* message
,
49 const base::Closure
& resume_callback
) {
50 DCHECK_EQ(state(), WAITING_MESSAGE
);
52 std::string method_attr
= message
->Attr(kMethodAttributeQName
);
53 AuthenticationMethod method
= AuthenticationMethod::FromString(method_attr
);
55 // The host picked a method different from the one the client had selected.
56 if (method
!= current_method_
) {
57 // The host must pick a method that is valid and supported by the client,
58 // and it must not change methods after it has picked one.
59 if (method_set_by_host_
|| !method
.is_valid() ||
60 std::find(methods_
.begin(), methods_
.end(), method
) == methods_
.end()) {
62 rejection_reason_
= PROTOCOL_ERROR
;
63 resume_callback
.Run();
67 current_method_
= method
;
68 method_set_by_host_
= true;
69 state_
= PROCESSING_MESSAGE
;
71 // Copy the message since the authenticator may process it asynchronously.
72 base::Closure callback
= base::Bind(
73 &NegotiatingAuthenticatorBase::ProcessMessageInternal
,
74 base::Unretained(this), base::Owned(new buzz::XmlElement(*message
)),
76 CreateAuthenticatorForCurrentMethod(WAITING_MESSAGE
, callback
);
79 ProcessMessageInternal(message
, resume_callback
);
82 scoped_ptr
<buzz::XmlElement
> NegotiatingClientAuthenticator::GetNextMessage() {
83 DCHECK_EQ(state(), MESSAGE_READY
);
85 // This is the first message to the host, send a list of supported methods.
86 if (!current_method_
.is_valid()) {
87 // If no authentication method has been chosen, see if we can optimistically
89 scoped_ptr
<buzz::XmlElement
> result
;
90 CreatePreferredAuthenticator();
91 if (current_authenticator_
) {
92 DCHECK(current_authenticator_
->state() == MESSAGE_READY
);
93 result
= GetNextMessageInternal();
95 result
= CreateEmptyAuthenticatorMessage();
98 // Include a list of supported methods.
99 std::stringstream
supported_methods(std::stringstream::out
);
100 for (std::vector
<AuthenticationMethod
>::iterator it
= methods_
.begin();
101 it
!= methods_
.end(); ++it
) {
102 if (it
!= methods_
.begin())
103 supported_methods
<< kSupportedMethodsSeparator
;
104 supported_methods
<< it
->ToString();
106 result
->AddAttr(kSupportedMethodsAttributeQName
, supported_methods
.str());
107 state_
= WAITING_MESSAGE
;
108 return result
.Pass();
110 return GetNextMessageInternal();
113 void NegotiatingClientAuthenticator::CreateAuthenticatorForCurrentMethod(
114 Authenticator::State preferred_initial_state
,
115 const base::Closure
& resume_callback
) {
116 DCHECK(current_method_
.is_valid());
117 if (current_method_
.type() == AuthenticationMethod::THIRD_PARTY
) {
118 // |ThirdPartyClientAuthenticator| takes ownership of |token_fetcher_|.
119 // The authentication method negotiation logic should guarantee that only
120 // one |ThirdPartyClientAuthenticator| will need to be created per session.
121 DCHECK(token_fetcher_
);
122 current_authenticator_
.reset(new ThirdPartyClientAuthenticator(
123 token_fetcher_
.Pass()));
124 resume_callback
.Run();
126 DCHECK(current_method_
.type() == AuthenticationMethod::SPAKE2
||
127 current_method_
.type() == AuthenticationMethod::SPAKE2_PAIR
);
128 bool pairing_supported
=
129 (current_method_
.type() == AuthenticationMethod::SPAKE2_PAIR
);
130 SecretFetchedCallback callback
= base::Bind(
131 &NegotiatingClientAuthenticator::CreateV2AuthenticatorWithSecret
,
132 weak_factory_
.GetWeakPtr(), preferred_initial_state
, resume_callback
);
133 fetch_secret_callback_
.Run(pairing_supported
, callback
);
137 void NegotiatingClientAuthenticator::CreatePreferredAuthenticator() {
138 if (!client_pairing_id_
.empty() && !shared_secret_
.empty() &&
139 std::find(methods_
.begin(), methods_
.end(),
140 AuthenticationMethod::Spake2Pair()) != methods_
.end()) {
141 // If the client specified a pairing id and shared secret, then create a
142 // PairingAuthenticator.
143 current_authenticator_
.reset(new PairingClientAuthenticator(
144 client_pairing_id_
, shared_secret_
, fetch_secret_callback_
,
145 authentication_tag_
));
146 current_method_
= AuthenticationMethod::Spake2Pair();
150 void NegotiatingClientAuthenticator::CreateV2AuthenticatorWithSecret(
151 Authenticator::State initial_state
,
152 const base::Closure
& resume_callback
,
153 const std::string
& shared_secret
) {
154 current_authenticator_
= V2Authenticator::CreateForClient(
155 AuthenticationMethod::ApplyHashFunction(
156 current_method_
.hash_function(), authentication_tag_
, shared_secret
),
158 resume_callback
.Run();
161 } // namespace protocol
162 } // namespace remoting