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