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" |
(...skipping 12 matching lines...) Expand all Loading... | |
23 | 23 |
24 const buzz::StaticQName kMethodAttributeQName = { "", "method" }; | 24 const buzz::StaticQName kMethodAttributeQName = { "", "method" }; |
25 const buzz::StaticQName kSupportedMethodsAttributeQName = | 25 const buzz::StaticQName kSupportedMethodsAttributeQName = |
26 { "", "supported-methods" }; | 26 { "", "supported-methods" }; |
27 | 27 |
28 const char kSupportedMethodsSeparator = ','; | 28 const char kSupportedMethodsSeparator = ','; |
29 | 29 |
30 } // namespace | 30 } // namespace |
31 | 31 |
32 // static | 32 // 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( | 33 scoped_ptr<Authenticator> NegotiatingAuthenticator::CreateForClient( |
40 const std::string& authentication_tag, | 34 const std::string& authentication_tag, |
41 const std::string& shared_secret, | 35 const FetchPinCallback& fetch_pin_callback, |
42 const std::vector<AuthenticationMethod>& methods) { | 36 const std::vector<AuthenticationMethod>& methods) { |
43 scoped_ptr<NegotiatingAuthenticator> result( | 37 scoped_ptr<NegotiatingAuthenticator> result( |
44 new NegotiatingAuthenticator(MESSAGE_READY)); | 38 new NegotiatingAuthenticator(MESSAGE_READY)); |
45 result->authentication_tag_ = authentication_tag; | 39 result->authentication_tag_ = authentication_tag; |
46 result->shared_secret_ = shared_secret; | 40 result->fetch_pin_callback_ = fetch_pin_callback; |
47 | 41 |
48 DCHECK(!methods.empty()); | 42 DCHECK(!methods.empty()); |
49 for (std::vector<AuthenticationMethod>::const_iterator it = methods.begin(); | 43 for (std::vector<AuthenticationMethod>::const_iterator it = methods.begin(); |
50 it != methods.end(); ++it) { | 44 it != methods.end(); ++it) { |
51 result->AddMethod(*it); | 45 result->AddMethod(*it); |
52 } | 46 } |
53 | 47 |
54 return scoped_ptr<Authenticator>(result.Pass()); | 48 return scoped_ptr<Authenticator>(result.Pass()); |
55 } | 49 } |
56 | 50 |
(...skipping 11 matching lines...) Expand all Loading... | |
68 | 62 |
69 result->AddMethod(AuthenticationMethod::Spake2(hash_function)); | 63 result->AddMethod(AuthenticationMethod::Spake2(hash_function)); |
70 | 64 |
71 return scoped_ptr<Authenticator>(result.Pass()); | 65 return scoped_ptr<Authenticator>(result.Pass()); |
72 } | 66 } |
73 | 67 |
74 NegotiatingAuthenticator::NegotiatingAuthenticator( | 68 NegotiatingAuthenticator::NegotiatingAuthenticator( |
75 Authenticator::State initial_state) | 69 Authenticator::State initial_state) |
76 : current_method_(AuthenticationMethod::Invalid()), | 70 : current_method_(AuthenticationMethod::Invalid()), |
77 state_(initial_state), | 71 state_(initial_state), |
78 rejection_reason_(INVALID_CREDENTIALS) { | 72 rejection_reason_(INVALID_CREDENTIALS), |
73 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
79 } | 74 } |
80 | 75 |
81 NegotiatingAuthenticator::~NegotiatingAuthenticator() { | 76 NegotiatingAuthenticator::~NegotiatingAuthenticator() { |
82 } | 77 } |
83 | 78 |
84 Authenticator::State NegotiatingAuthenticator::state() const { | 79 Authenticator::State NegotiatingAuthenticator::state() const { |
85 return state_; | 80 return state_; |
86 } | 81 } |
87 | 82 |
88 Authenticator::RejectionReason | 83 Authenticator::RejectionReason |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
138 if (!method.is_valid()) { | 133 if (!method.is_valid()) { |
139 // Failed to find a common auth method. | 134 // Failed to find a common auth method. |
140 state_ = REJECTED; | 135 state_ = REJECTED; |
141 rejection_reason_ = PROTOCOL_ERROR; | 136 rejection_reason_ = PROTOCOL_ERROR; |
142 resume_callback.Run(); | 137 resume_callback.Run(); |
143 return; | 138 return; |
144 } | 139 } |
145 | 140 |
146 // Drop the current message because we've chosen a different | 141 // Drop the current message because we've chosen a different |
147 // method. | 142 // method. |
148 state_ = MESSAGE_READY; | 143 current_method_ = method; |
144 state_ = PROCESSING_MESSAGE; | |
145 CreateAuthenticator(MESSAGE_READY, base::Bind( | |
146 &NegotiatingAuthenticator::UpdateState, | |
147 base::Unretained(this), resume_callback)); | |
148 } else { | |
149 if (method != current_method_) { | |
150 current_method_ = method; | |
151 state_ = PROCESSING_MESSAGE; | |
152 // Copy the message since the authenticator may process it asynchronously. | |
153 CreateAuthenticator(WAITING_MESSAGE, base::Bind( | |
154 &NegotiatingAuthenticator::ProcessMessageInternal, | |
155 base::Unretained(this), base::Owned(new buzz::XmlElement(*message)), | |
156 resume_callback)); | |
157 } else { | |
158 ProcessMessageInternal(message, resume_callback); | |
159 } | |
149 } | 160 } |
161 } | |
150 | 162 |
151 DCHECK(method.is_valid()); | 163 void NegotiatingAuthenticator::ProcessMessageInternal( |
152 | 164 const buzz::XmlElement* message, |
153 // Replace current authenticator if the method has changed. | 165 const base::Closure& resume_callback) { |
154 if (method != current_method_) { | 166 if (current_authenticator_->state() == WAITING_MESSAGE) { |
155 current_method_ = method; | 167 // If the message was not discarded and the authenticator is waiting for it, |
156 CreateAuthenticator(state_); | 168 // give it to the underlying authenticator to process. |
157 } | |
158 if (state_ == WAITING_MESSAGE) { | |
159 // |current_authenticator_| is owned, so Unretained() is safe here. | 169 // |current_authenticator_| is owned, so Unretained() is safe here. |
160 current_authenticator_->ProcessMessage(message, base::Bind( | 170 current_authenticator_->ProcessMessage(message, base::Bind( |
161 &NegotiatingAuthenticator::UpdateState, | 171 &NegotiatingAuthenticator::UpdateState, |
162 base::Unretained(this), resume_callback)); | 172 base::Unretained(this), resume_callback)); |
163 } else { | |
164 UpdateState(resume_callback); | |
165 } | 173 } |
166 } | 174 } |
167 | 175 |
168 void NegotiatingAuthenticator::UpdateState( | 176 void NegotiatingAuthenticator::UpdateState( |
169 const base::Closure& resume_callback) { | 177 const base::Closure& resume_callback) { |
170 // After the underlying authenticator finishes processing the message, the | 178 // After the underlying authenticator finishes processing the message, the |
171 // NegotiatingAuthenticator must update its own state before running the | 179 // NegotiatingAuthenticator must update its own state before running the |
172 // |resume_callback| to resume the session negotiation. | 180 // |resume_callback| to resume the session negotiation. |
173 state_ = current_authenticator_->state(); | 181 state_ = current_authenticator_->state(); |
174 if (state_ == REJECTED) | 182 if (state_ == REJECTED) |
175 rejection_reason_ = current_authenticator_->rejection_reason(); | 183 rejection_reason_ = current_authenticator_->rejection_reason(); |
176 resume_callback.Run(); | 184 resume_callback.Run(); |
177 } | 185 } |
178 | 186 |
179 scoped_ptr<buzz::XmlElement> NegotiatingAuthenticator::GetNextMessage() { | 187 scoped_ptr<buzz::XmlElement> NegotiatingAuthenticator::GetNextMessage() { |
180 DCHECK_EQ(state(), MESSAGE_READY); | 188 DCHECK_EQ(state(), MESSAGE_READY); |
181 | 189 |
182 bool add_supported_methods_attr = false; | 190 scoped_ptr<buzz::XmlElement> result; |
183 | 191 |
184 // Initialize current method in case it is not initialized | 192 // No method yet, just send a message with the list of supported methods. |
185 // yet. Normally happens only on client. | 193 // Normally happens only on client. |
186 if (!current_method_.is_valid()) { | 194 if (!current_method_.is_valid()) { |
187 CHECK(!methods_.empty()); | 195 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); | 196 std::stringstream supported_methods(std::stringstream::out); |
204 for (std::vector<AuthenticationMethod>::iterator it = methods_.begin(); | 197 for (std::vector<AuthenticationMethod>::iterator it = methods_.begin(); |
205 it != methods_.end(); ++it) { | 198 it != methods_.end(); ++it) { |
206 if (it != methods_.begin()) | 199 if (it != methods_.begin()) |
207 supported_methods << kSupportedMethodsSeparator; | 200 supported_methods << kSupportedMethodsSeparator; |
208 supported_methods << it->ToString(); | 201 supported_methods << it->ToString(); |
209 } | 202 } |
210 result->AddAttr(kSupportedMethodsAttributeQName, supported_methods.str()); | 203 result->AddAttr(kSupportedMethodsAttributeQName, supported_methods.str()); |
204 state_ = WAITING_MESSAGE; | |
205 } else { | |
206 if (current_authenticator_->state() == MESSAGE_READY) { | |
207 result = current_authenticator_->GetNextMessage(); | |
208 } else { | |
209 result = CreateEmptyAuthenticatorMessage(); | |
210 } | |
211 state_ = current_authenticator_->state(); | |
212 DCHECK_NE(state_, REJECTED); | |
213 DCHECK_NE(state_, MESSAGE_READY); | |
214 result->AddAttr(kMethodAttributeQName, current_method_.ToString()); | |
211 } | 215 } |
212 | 216 |
213 return result.Pass(); | 217 return result.Pass(); |
214 } | 218 } |
215 | 219 |
216 void NegotiatingAuthenticator::AddMethod(const AuthenticationMethod& method) { | 220 void NegotiatingAuthenticator::AddMethod(const AuthenticationMethod& method) { |
217 DCHECK(method.is_valid()); | 221 DCHECK(method.is_valid()); |
218 methods_.push_back(method); | 222 methods_.push_back(method); |
219 } | 223 } |
220 | 224 |
221 scoped_ptr<ChannelAuthenticator> | 225 scoped_ptr<ChannelAuthenticator> |
222 NegotiatingAuthenticator::CreateChannelAuthenticator() const { | 226 NegotiatingAuthenticator::CreateChannelAuthenticator() const { |
223 DCHECK_EQ(state(), ACCEPTED); | 227 DCHECK_EQ(state(), ACCEPTED); |
224 return current_authenticator_->CreateChannelAuthenticator(); | 228 return current_authenticator_->CreateChannelAuthenticator(); |
225 } | 229 } |
226 | 230 |
227 bool NegotiatingAuthenticator::is_host_side() const { | 231 bool NegotiatingAuthenticator::is_host_side() const { |
228 return local_key_pair_.get() != NULL; | 232 return local_key_pair_.get() != NULL; |
229 } | 233 } |
230 | 234 |
231 void NegotiatingAuthenticator::CreateAuthenticator(State initial_state) { | 235 |
236 void NegotiatingAuthenticator::CreateAuthenticator( | |
237 Authenticator::State preferred_initial_state, | |
238 const base::Closure& resume_callback) { | |
232 if (is_host_side()) { | 239 if (is_host_side()) { |
233 current_authenticator_ = V2Authenticator::CreateForHost( | 240 current_authenticator_ = V2Authenticator::CreateForHost( |
234 local_cert_, local_key_pair_, shared_secret_hash_, initial_state); | 241 local_cert_, local_key_pair_, shared_secret_hash_, |
242 preferred_initial_state); | |
243 resume_callback.Run(); | |
235 } else { | 244 } else { |
236 current_authenticator_ = V2Authenticator::CreateForClient( | 245 fetch_pin_callback_.Run(base::Bind( |
237 AuthenticationMethod::ApplyHashFunction( | 246 &NegotiatingAuthenticator::OnPinFetched, |
238 current_method_.hash_function(), | 247 weak_factory_.GetWeakPtr(), preferred_initial_state, resume_callback)); |
Wez
2013/03/20 01:36:31
This is a strange way to arrange things.
Why can'
rmsousa
2013/03/20 04:54:54
The V2Authenticator is unusable until it has a sec
| |
239 authentication_tag_, shared_secret_), initial_state); | |
240 } | 248 } |
241 } | 249 } |
242 | 250 |
251 void NegotiatingAuthenticator::OnPinFetched( | |
252 Authenticator::State initial_state, | |
253 const base::Closure& resume_callback, | |
254 const std::string& shared_secret) { | |
255 current_authenticator_ = V2Authenticator::CreateForClient( | |
256 AuthenticationMethod::ApplyHashFunction( | |
257 current_method_.hash_function(), authentication_tag_, shared_secret), | |
258 initial_state); | |
259 resume_callback.Run(); | |
Wez
2013/03/20 01:36:31
Why does this need a |resume_callback|?
rmsousa
2013/03/20 04:54:54
All of this is happening while the negotiatingauth
| |
260 } | |
261 | |
243 } // namespace protocol | 262 } // namespace protocol |
244 } // namespace remoting | 263 } // namespace remoting |
OLD | NEW |