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.
4
5 #include "remoting/protocol/negotiating_host_authenticator.h"
6
7 #include <algorithm>
8 #include <sstream>
9
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"
20
21 namespace remoting {
22 namespace protocol {
23
NegotiatingHostAuthenticator(const std::string & local_cert,scoped_refptr<RsaKeyPair> key_pair)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) {
30 }
31
32 // static
CreateWithSharedSecret(const std::string & local_cert,scoped_refptr<RsaKeyPair> key_pair,const std::string & shared_secret_hash,AuthenticationMethod::HashFunction hash_function,scoped_refptr<PairingRegistry> pairing_registry)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());
46 }
47 return scoped_ptr<Authenticator>(result.Pass());
48 }
49
50 // static
51 scoped_ptr<Authenticator>
CreateWithThirdPartyAuth(const std::string & local_cert,scoped_refptr<RsaKeyPair> key_pair,scoped_ptr<ThirdPartyHostAuthenticator::TokenValidator> token_validator)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());
61 }
62
~NegotiatingHostAuthenticator()63 NegotiatingHostAuthenticator::~NegotiatingHostAuthenticator() {
64 }
65
ProcessMessage(const buzz::XmlElement * message,const base::Closure & resume_callback)66 void NegotiatingHostAuthenticator::ProcessMessage(
67 const buzz::XmlElement* message,
68 const base::Closure& resume_callback) {
69 DCHECK_EQ(state(), WAITING_MESSAGE);
70
71 std::string method_attr = message->Attr(kMethodAttributeQName);
72 AuthenticationMethod method = AuthenticationMethod::FromString(method_attr);
73
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_) {
76 state_ = REJECTED;
77 rejection_reason_ = PROTOCOL_ERROR;
78 resume_callback.Run();
79 return;
80 }
81
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();
88
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.
93 state_ = REJECTED;
94 rejection_reason_ = PROTOCOL_ERROR;
95 resume_callback.Run();
96 return;
97 }
98
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.
111 method = list_value;
112 break;
113 }
114 }
115
116 if (!method.is_valid()) {
117 // Failed to find a common auth method.
118 state_ = REJECTED;
119 rejection_reason_ = PROTOCOL_ERROR;
120 resume_callback.Run();
121 return;
122 }
123
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));
130 return;
131 }
132
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)),
142 resume_callback));
143 return;
144 }
145
146 // If the client is using the host's current method, just process the message.
147 ProcessMessageInternal(message, resume_callback);
148 }
149
GetNextMessage()150 scoped_ptr<buzz::XmlElement> NegotiatingHostAuthenticator::GetNextMessage() {
151 return GetNextMessageInternal();
152 }
153
CreateAuthenticator(Authenticator::State preferred_initial_state,const base::Closure & resume_callback)154 void NegotiatingHostAuthenticator::CreateAuthenticator(
155 Authenticator::State preferred_initial_state,
156 const base::Closure& resume_callback) {
157 DCHECK(current_method_.is_valid());
158
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_));
172 } else {
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
178 // the PIN.
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);
184 }
185 resume_callback.Run();
186 }
187
188 } // namespace protocol
189 } // namespace remoting
190