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_host_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/base/rsa_key_pair.h"
15 #include "remoting/protocol/channel_authenticator.h"
16 #include "remoting/protocol/pairing_host_authenticator.h"
17 #include "remoting/protocol/pairing_registry.h"
18 #include "remoting/protocol/v2_authenticator.h"
19 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
24 NegotiatingHostAuthenticator::NegotiatingHostAuthenticator(
25 const std::string
& local_cert
,
26 scoped_refptr
<RsaKeyPair
> key_pair
)
27 : NegotiatingAuthenticatorBase(WAITING_MESSAGE
),
28 local_cert_(local_cert
),
29 local_key_pair_(key_pair
) {
33 scoped_ptr
<Authenticator
> NegotiatingHostAuthenticator::CreateWithSharedSecret(
34 const std::string
& local_cert
,
35 scoped_refptr
<RsaKeyPair
> key_pair
,
36 const std::string
& shared_secret_hash
,
37 AuthenticationMethod::HashFunction hash_function
,
38 scoped_refptr
<PairingRegistry
> pairing_registry
) {
39 scoped_ptr
<NegotiatingHostAuthenticator
> result(
40 new NegotiatingHostAuthenticator(local_cert
, key_pair
));
41 result
->shared_secret_hash_
= shared_secret_hash
;
42 result
->pairing_registry_
= pairing_registry
;
43 result
->AddMethod(AuthenticationMethod::Spake2(hash_function
));
44 if (pairing_registry
.get()) {
45 result
->AddMethod(AuthenticationMethod::Spake2Pair());
47 return scoped_ptr
<Authenticator
>(result
.Pass());
51 scoped_ptr
<Authenticator
>
52 NegotiatingHostAuthenticator::CreateWithThirdPartyAuth(
53 const std::string
& local_cert
,
54 scoped_refptr
<RsaKeyPair
> key_pair
,
55 scoped_ptr
<ThirdPartyHostAuthenticator::TokenValidator
> token_validator
) {
56 scoped_ptr
<NegotiatingHostAuthenticator
> result(
57 new NegotiatingHostAuthenticator(local_cert
, key_pair
));
58 result
->token_validator_
= token_validator
.Pass();
59 result
->AddMethod(AuthenticationMethod::ThirdParty());
60 return scoped_ptr
<Authenticator
>(result
.Pass());
63 NegotiatingHostAuthenticator::~NegotiatingHostAuthenticator() {
66 void NegotiatingHostAuthenticator::ProcessMessage(
67 const buzz::XmlElement
* message
,
68 const base::Closure
& resume_callback
) {
69 DCHECK_EQ(state(), WAITING_MESSAGE
);
71 std::string method_attr
= message
->Attr(kMethodAttributeQName
);
72 AuthenticationMethod method
= AuthenticationMethod::FromString(method_attr
);
74 // If the host has already chosen a method, it can't be changed by the client.
75 if (current_method_
.is_valid() && method
!= current_method_
) {
77 rejection_reason_
= PROTOCOL_ERROR
;
78 resume_callback
.Run();
82 // If the client did not specify a preferred auth method, or specified an
83 // unknown or unsupported method, then select the first known method from
84 // the supported-methods attribute.
85 if (!method
.is_valid() ||
86 std::find(methods_
.begin(), methods_
.end(), method
) == methods_
.end()) {
87 method
= AuthenticationMethod::Invalid();
89 std::string supported_methods_attr
=
90 message
->Attr(kSupportedMethodsAttributeQName
);
91 if (supported_methods_attr
.empty()) {
92 // Message contains neither method nor supported-methods attributes.
94 rejection_reason_
= PROTOCOL_ERROR
;
95 resume_callback
.Run();
99 // Find the first mutually-supported method in the client's list of
100 // supported-methods.
101 std::vector
<std::string
> supported_methods_strs
;
102 base::SplitString(supported_methods_attr
, kSupportedMethodsSeparator
,
103 &supported_methods_strs
);
104 for (std::vector
<std::string
>::iterator it
= supported_methods_strs
.begin();
105 it
!= supported_methods_strs
.end(); ++it
) {
106 AuthenticationMethod list_value
= AuthenticationMethod::FromString(*it
);
107 if (list_value
.is_valid() &&
108 std::find(methods_
.begin(),
109 methods_
.end(), list_value
) != methods_
.end()) {
110 // Found common method.
116 if (!method
.is_valid()) {
117 // Failed to find a common auth method.
119 rejection_reason_
= PROTOCOL_ERROR
;
120 resume_callback
.Run();
124 // Drop the current message because we've chosen a different method.
125 current_method_
= method
;
126 state_
= PROCESSING_MESSAGE
;
127 CreateAuthenticator(MESSAGE_READY
, base::Bind(
128 &NegotiatingHostAuthenticator::UpdateState
,
129 base::Unretained(this), resume_callback
));
133 // If the client specified a supported method, and the host hasn't chosen a
134 // method yet, use the client's preferred method and process the message.
135 if (!current_method_
.is_valid()) {
136 current_method_
= method
;
137 state_
= PROCESSING_MESSAGE
;
138 // Copy the message since the authenticator may process it asynchronously.
139 CreateAuthenticator(WAITING_MESSAGE
, base::Bind(
140 &NegotiatingAuthenticatorBase::ProcessMessageInternal
,
141 base::Unretained(this), base::Owned(new buzz::XmlElement(*message
)),
146 // If the client is using the host's current method, just process the message.
147 ProcessMessageInternal(message
, resume_callback
);
150 scoped_ptr
<buzz::XmlElement
> NegotiatingHostAuthenticator::GetNextMessage() {
151 return GetNextMessageInternal();
154 void NegotiatingHostAuthenticator::CreateAuthenticator(
155 Authenticator::State preferred_initial_state
,
156 const base::Closure
& resume_callback
) {
157 DCHECK(current_method_
.is_valid());
159 if (current_method_
.type() == AuthenticationMethod::THIRD_PARTY
) {
160 // |ThirdPartyHostAuthenticator| takes ownership of |token_validator_|.
161 // The authentication method negotiation logic should guarantee that only
162 // one |ThirdPartyHostAuthenticator| will need to be created per session.
163 DCHECK(token_validator_
);
164 current_authenticator_
.reset(new ThirdPartyHostAuthenticator(
165 local_cert_
, local_key_pair_
, token_validator_
.Pass()));
166 } else if (current_method_
== AuthenticationMethod::Spake2Pair() &&
167 preferred_initial_state
== WAITING_MESSAGE
) {
168 // If the client requested Spake2Pair and sent an initial message, attempt
169 // the paired connection protocol.
170 current_authenticator_
.reset(new PairingHostAuthenticator(
171 pairing_registry_
, local_cert_
, local_key_pair_
, shared_secret_hash_
));
173 // In all other cases, use the V2 protocol. Note that this includes the
174 // case where the protocol is Spake2Pair but the client is not yet paired.
175 // In this case, the on-the-wire protocol is plain Spake2, advertised as
176 // Spake2Pair so that the client knows that the host supports pairing and
177 // that it can therefore present the option to the user when they enter
179 DCHECK(current_method_
.type() == AuthenticationMethod::SPAKE2
||
180 current_method_
.type() == AuthenticationMethod::SPAKE2_PAIR
);
181 current_authenticator_
= V2Authenticator::CreateForHost(
182 local_cert_
, local_key_pair_
, shared_secret_hash_
,
183 preferred_initial_state
);
185 resume_callback
.Run();
188 } // namespace protocol
189 } // namespace remoting