OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/protocol/negotiating_authenticator.h" | 5 #include "remoting/protocol/negotiating_authenticator.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <sstream> | 8 #include <sstream> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/strings/string_split.h" | 13 #include "base/strings/string_split.h" |
14 #include "remoting/base/rsa_key_pair.h" | 14 #include "remoting/base/rsa_key_pair.h" |
15 #include "remoting/protocol/channel_authenticator.h" | 15 #include "remoting/protocol/channel_authenticator.h" |
16 #include "remoting/protocol/v2_authenticator.h" | 16 #include "remoting/protocol/v2_authenticator.h" |
17 #include "remoting/protocol/pin_client_authenticator.h" | |
18 #include "remoting/protocol/pin_fetcher_factory.h" | |
17 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" | 19 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" |
18 | 20 |
19 namespace remoting { | 21 namespace remoting { |
20 namespace protocol { | 22 namespace protocol { |
21 | 23 |
22 namespace { | 24 namespace { |
23 | 25 |
24 const buzz::StaticQName kMethodAttributeQName = { "", "method" }; | 26 const buzz::StaticQName kMethodAttributeQName = { "", "method" }; |
25 const buzz::StaticQName kSupportedMethodsAttributeQName = | 27 const buzz::StaticQName kSupportedMethodsAttributeQName = |
26 { "", "supported-methods" }; | 28 { "", "supported-methods" }; |
27 | 29 |
28 const char kSupportedMethodsSeparator = ','; | 30 const char kSupportedMethodsSeparator = ','; |
29 | 31 |
30 } // namespace | 32 } // namespace |
31 | 33 |
32 // static | 34 // static |
33 bool NegotiatingAuthenticator::IsNegotiableMessage( | |
34 const buzz::XmlElement* message) { | |
35 return message->HasAttr(kSupportedMethodsAttributeQName); | |
36 } | |
37 | |
38 // static | |
39 scoped_ptr<Authenticator> NegotiatingAuthenticator::CreateForClient( | 35 scoped_ptr<Authenticator> NegotiatingAuthenticator::CreateForClient( |
40 const std::string& authentication_tag, | 36 const std::string& authentication_tag, |
41 const std::string& shared_secret, | 37 const std::string& shared_secret, |
38 PinFetcherFactory* pin_fetcher_factory, | |
42 const std::vector<AuthenticationMethod>& methods) { | 39 const std::vector<AuthenticationMethod>& methods) { |
43 scoped_ptr<NegotiatingAuthenticator> result( | 40 scoped_ptr<NegotiatingAuthenticator> result( |
44 new NegotiatingAuthenticator(MESSAGE_READY)); | 41 new NegotiatingAuthenticator(MESSAGE_READY)); |
45 result->authentication_tag_ = authentication_tag; | 42 result->authentication_tag_ = authentication_tag; |
46 result->shared_secret_ = shared_secret; | 43 result->shared_secret_ = shared_secret; |
44 result->pin_fetcher_factory_ = pin_fetcher_factory; | |
47 | 45 |
48 DCHECK(!methods.empty()); | 46 DCHECK(!methods.empty()); |
49 for (std::vector<AuthenticationMethod>::const_iterator it = methods.begin(); | 47 for (std::vector<AuthenticationMethod>::const_iterator it = methods.begin(); |
50 it != methods.end(); ++it) { | 48 it != methods.end(); ++it) { |
51 result->AddMethod(*it); | 49 result->AddMethod(*it); |
52 } | 50 } |
53 | 51 |
54 return scoped_ptr<Authenticator>(result.Pass()); | 52 return scoped_ptr<Authenticator>(result.Pass()); |
55 } | 53 } |
56 | 54 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
138 if (!method.is_valid()) { | 136 if (!method.is_valid()) { |
139 // Failed to find a common auth method. | 137 // Failed to find a common auth method. |
140 state_ = REJECTED; | 138 state_ = REJECTED; |
141 rejection_reason_ = PROTOCOL_ERROR; | 139 rejection_reason_ = PROTOCOL_ERROR; |
142 resume_callback.Run(); | 140 resume_callback.Run(); |
143 return; | 141 return; |
144 } | 142 } |
145 | 143 |
146 // Drop the current message because we've chosen a different | 144 // Drop the current message because we've chosen a different |
147 // method. | 145 // method. |
148 state_ = MESSAGE_READY; | 146 current_method_ = method; |
147 state_ = PROCESSING_MESSAGE; | |
148 CreateAuthenticator(MESSAGE_READY, base::Bind( | |
149 &NegotiatingAuthenticator::UpdateState, | |
150 base::Unretained(this), resume_callback)); | |
151 } else { | |
152 if (method != current_method_) { | |
153 current_method_ = method; | |
154 state_ = PROCESSING_MESSAGE; | |
155 // Copy the message since the authenticator may process it asynchronously. | |
156 CreateAuthenticator(WAITING_MESSAGE, base::Bind( | |
157 &NegotiatingAuthenticator::ProcessMessageInternal, | |
158 base::Unretained(this), base::Owned(new buzz::XmlElement(*message)), | |
159 resume_callback)); | |
160 } else { | |
161 ProcessMessageInternal(message, resume_callback); | |
162 } | |
149 } | 163 } |
164 } | |
150 | 165 |
151 DCHECK(method.is_valid()); | 166 void NegotiatingAuthenticator::ProcessMessageInternal( |
152 | 167 const buzz::XmlElement* message, |
153 // Replace current authenticator if the method has changed. | 168 const base::Closure& resume_callback) { |
154 if (method != current_method_) { | 169 if (current_authenticator_->state() == WAITING_MESSAGE) { |
155 current_method_ = method; | 170 // If the message was not discarded and the authenticator is waiting for it, |
156 CreateAuthenticator(state_); | 171 // give it to the underlying authenticator to process. |
157 } | |
158 if (state_ == WAITING_MESSAGE) { | |
159 // |current_authenticator_| is owned, so Unretained() is safe here. | 172 // |current_authenticator_| is owned, so Unretained() is safe here. |
160 current_authenticator_->ProcessMessage(message, base::Bind( | 173 current_authenticator_->ProcessMessage(message, base::Bind( |
161 &NegotiatingAuthenticator::UpdateState, | 174 &NegotiatingAuthenticator::UpdateState, |
162 base::Unretained(this), resume_callback)); | 175 base::Unretained(this), resume_callback)); |
163 } else { | |
164 UpdateState(resume_callback); | |
165 } | 176 } |
166 } | 177 } |
167 | 178 |
168 void NegotiatingAuthenticator::UpdateState( | 179 void NegotiatingAuthenticator::UpdateState( |
169 const base::Closure& resume_callback) { | 180 const base::Closure& resume_callback) { |
170 // After the underlying authenticator finishes processing the message, the | 181 // After the underlying authenticator finishes processing the message, the |
171 // NegotiatingAuthenticator must update its own state before running the | 182 // NegotiatingAuthenticator must update its own state before running the |
172 // |resume_callback| to resume the session negotiation. | 183 // |resume_callback| to resume the session negotiation. |
173 state_ = current_authenticator_->state(); | 184 state_ = current_authenticator_->state(); |
174 if (state_ == REJECTED) | 185 if (state_ == REJECTED) |
175 rejection_reason_ = current_authenticator_->rejection_reason(); | 186 rejection_reason_ = current_authenticator_->rejection_reason(); |
176 resume_callback.Run(); | 187 resume_callback.Run(); |
177 } | 188 } |
178 | 189 |
179 scoped_ptr<buzz::XmlElement> NegotiatingAuthenticator::GetNextMessage() { | 190 scoped_ptr<buzz::XmlElement> NegotiatingAuthenticator::GetNextMessage() { |
180 DCHECK_EQ(state(), MESSAGE_READY); | 191 DCHECK_EQ(state(), MESSAGE_READY); |
181 | 192 |
182 bool add_supported_methods_attr = false; | 193 scoped_ptr<buzz::XmlElement> result; |
183 | 194 |
184 // Initialize current method in case it is not initialized | 195 // No method yet, just send a message with the list of supported methods. |
185 // yet. Normally happens only on client. | 196 // Normally happens only on client. |
186 if (!current_method_.is_valid()) { | 197 if (!current_method_.is_valid()) { |
187 CHECK(!methods_.empty()); | 198 result = CreateEmptyAuthenticatorMessage(); |
188 | |
189 // Initially try the first method. | |
190 current_method_ = methods_[0]; | |
191 CreateAuthenticator(MESSAGE_READY); | |
192 add_supported_methods_attr = true; | |
193 } | |
194 | |
195 scoped_ptr<buzz::XmlElement> result = | |
196 current_authenticator_->GetNextMessage(); | |
197 state_ = current_authenticator_->state(); | |
198 DCHECK_NE(state_, REJECTED); | |
199 | |
200 result->AddAttr(kMethodAttributeQName, current_method_.ToString()); | |
201 | |
202 if (add_supported_methods_attr) { | |
203 std::stringstream supported_methods(std::stringstream::out); | 199 std::stringstream supported_methods(std::stringstream::out); |
204 for (std::vector<AuthenticationMethod>::iterator it = methods_.begin(); | 200 for (std::vector<AuthenticationMethod>::iterator it = methods_.begin(); |
205 it != methods_.end(); ++it) { | 201 it != methods_.end(); ++it) { |
206 if (it != methods_.begin()) | 202 if (it != methods_.begin()) |
207 supported_methods << kSupportedMethodsSeparator; | 203 supported_methods << kSupportedMethodsSeparator; |
208 supported_methods << it->ToString(); | 204 supported_methods << it->ToString(); |
209 } | 205 } |
210 result->AddAttr(kSupportedMethodsAttributeQName, supported_methods.str()); | 206 result->AddAttr(kSupportedMethodsAttributeQName, supported_methods.str()); |
207 state_ = WAITING_MESSAGE; | |
208 } else { | |
209 if (current_authenticator_->state() == MESSAGE_READY) { | |
210 result = current_authenticator_->GetNextMessage(); | |
211 } else { | |
212 result = CreateEmptyAuthenticatorMessage(); | |
213 } | |
214 state_ = current_authenticator_->state(); | |
215 DCHECK_NE(state_, REJECTED); | |
216 DCHECK_NE(state_, MESSAGE_READY); | |
217 result->AddAttr(kMethodAttributeQName, current_method_.ToString()); | |
211 } | 218 } |
212 | 219 |
213 return result.Pass(); | 220 return result.Pass(); |
214 } | 221 } |
215 | 222 |
216 void NegotiatingAuthenticator::AddMethod(const AuthenticationMethod& method) { | 223 void NegotiatingAuthenticator::AddMethod(const AuthenticationMethod& method) { |
217 DCHECK(method.is_valid()); | 224 DCHECK(method.is_valid()); |
218 methods_.push_back(method); | 225 methods_.push_back(method); |
219 } | 226 } |
220 | 227 |
221 scoped_ptr<ChannelAuthenticator> | 228 scoped_ptr<ChannelAuthenticator> |
222 NegotiatingAuthenticator::CreateChannelAuthenticator() const { | 229 NegotiatingAuthenticator::CreateChannelAuthenticator() const { |
223 DCHECK_EQ(state(), ACCEPTED); | 230 DCHECK_EQ(state(), ACCEPTED); |
224 return current_authenticator_->CreateChannelAuthenticator(); | 231 return current_authenticator_->CreateChannelAuthenticator(); |
225 } | 232 } |
226 | 233 |
227 bool NegotiatingAuthenticator::is_host_side() const { | 234 bool NegotiatingAuthenticator::is_host_side() const { |
228 return local_key_pair_.get() != NULL; | 235 return local_key_pair_.get() != NULL; |
229 } | 236 } |
230 | 237 |
231 void NegotiatingAuthenticator::CreateAuthenticator(State initial_state) { | 238 |
239 void NegotiatingAuthenticator::CreateAuthenticator( | |
240 Authenticator::State preferred_initial_state, | |
241 const base::Closure& resume_callback) { | |
232 if (is_host_side()) { | 242 if (is_host_side()) { |
233 current_authenticator_ = V2Authenticator::CreateForHost( | 243 current_authenticator_ = V2Authenticator::CreateForHost( |
234 local_cert_, local_key_pair_, shared_secret_hash_, initial_state); | 244 local_cert_, local_key_pair_, shared_secret_hash_, |
245 preferred_initial_state); | |
235 } else { | 246 } else { |
236 current_authenticator_ = V2Authenticator::CreateForClient( | 247 if (pin_fetcher_factory_) { |
Sergey Ulanov
2013/03/17 21:29:21
Do we really need to distinguish between these two
rmsousa
2013/03/18 21:07:26
I think that would get ugly in the upper layers, w
Sergey Ulanov
2013/03/19 02:44:53
I think this is just a question of whether we shou
rmsousa
2013/03/19 23:14:31
Done.
| |
237 AuthenticationMethod::ApplyHashFunction( | 248 // No pre-entered secret, need to use the interactive authenticator. |
238 current_method_.hash_function(), | 249 current_authenticator_.reset(new PinClientAuthenticator( |
239 authentication_tag_, shared_secret_), initial_state); | 250 current_method_.hash_function(), authentication_tag_, |
251 pin_fetcher_factory_->CreatePinFetcher(), preferred_initial_state, | |
Sergey Ulanov
2013/03/17 21:29:21
Why do we need the factory interface? Is there any
rmsousa
2013/03/18 21:07:26
It's to manage the lifetime for the callback appro
Sergey Ulanov
2013/03/19 02:44:53
The issue with possible lifetime mismatch is easy
rmsousa
2013/03/19 23:14:31
Done.
| |
252 resume_callback)); | |
253 return; | |
254 } else { | |
255 // Pre-entered secret (legacy client), just proceed with V2 authenticator. | |
256 current_authenticator_ = V2Authenticator::CreateForClient( | |
257 AuthenticationMethod::ApplyHashFunction( | |
258 current_method_.hash_function(), | |
259 authentication_tag_, shared_secret_), preferred_initial_state); | |
Sergey Ulanov
2013/03/17 21:29:21
nit: move the last parameter to a separate line fo
rmsousa
2013/03/18 21:07:26
Done.
| |
260 } | |
240 } | 261 } |
262 resume_callback.Run(); | |
241 } | 263 } |
242 | 264 |
243 } // namespace protocol | 265 } // namespace protocol |
244 } // namespace remoting | 266 } // namespace remoting |
OLD | NEW |