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 FetchSecretCallback& fetch_secret_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_secret_callback_ = fetch_secret_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 return; |
149 } | 149 } |
150 | |
151 DCHECK(method.is_valid()); | |
152 | |
153 // Replace current authenticator if the method has changed. | |
154 if (method != current_method_) { | 150 if (method != current_method_) { |
155 current_method_ = method; | 151 current_method_ = method; |
156 CreateAuthenticator(state_); | 152 state_ = PROCESSING_MESSAGE; |
| 153 // Copy the message since the authenticator may process it asynchronously. |
| 154 CreateAuthenticator(WAITING_MESSAGE, base::Bind( |
| 155 &NegotiatingAuthenticator::ProcessMessageInternal, |
| 156 base::Unretained(this), base::Owned(new buzz::XmlElement(*message)), |
| 157 resume_callback)); |
| 158 return; |
157 } | 159 } |
158 if (state_ == WAITING_MESSAGE) { | 160 ProcessMessageInternal(message, resume_callback); |
159 // |current_authenticator_| is owned, so Unretained() is safe here. | |
160 current_authenticator_->ProcessMessage(message, base::Bind( | |
161 &NegotiatingAuthenticator::UpdateState, | |
162 base::Unretained(this), resume_callback)); | |
163 } else { | |
164 UpdateState(resume_callback); | |
165 } | |
166 } | 161 } |
167 | 162 |
168 void NegotiatingAuthenticator::UpdateState( | 163 void NegotiatingAuthenticator::UpdateState( |
169 const base::Closure& resume_callback) { | 164 const base::Closure& resume_callback) { |
170 // After the underlying authenticator finishes processing the message, the | 165 // After the underlying authenticator finishes processing the message, the |
171 // NegotiatingAuthenticator must update its own state before running the | 166 // NegotiatingAuthenticator must update its own state before running the |
172 // |resume_callback| to resume the session negotiation. | 167 // |resume_callback| to resume the session negotiation. |
173 state_ = current_authenticator_->state(); | 168 state_ = current_authenticator_->state(); |
174 if (state_ == REJECTED) | 169 if (state_ == REJECTED) |
175 rejection_reason_ = current_authenticator_->rejection_reason(); | 170 rejection_reason_ = current_authenticator_->rejection_reason(); |
176 resume_callback.Run(); | 171 resume_callback.Run(); |
177 } | 172 } |
178 | 173 |
179 scoped_ptr<buzz::XmlElement> NegotiatingAuthenticator::GetNextMessage() { | 174 scoped_ptr<buzz::XmlElement> NegotiatingAuthenticator::GetNextMessage() { |
180 DCHECK_EQ(state(), MESSAGE_READY); | 175 DCHECK_EQ(state(), MESSAGE_READY); |
181 | 176 |
182 bool add_supported_methods_attr = false; | 177 scoped_ptr<buzz::XmlElement> result; |
183 | 178 |
184 // Initialize current method in case it is not initialized | 179 // No method yet, just send a message with the list of supported methods. |
185 // yet. Normally happens only on client. | 180 // Normally happens only on client. |
186 if (!current_method_.is_valid()) { | 181 if (!current_method_.is_valid()) { |
187 CHECK(!methods_.empty()); | 182 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); | 183 std::stringstream supported_methods(std::stringstream::out); |
204 for (std::vector<AuthenticationMethod>::iterator it = methods_.begin(); | 184 for (std::vector<AuthenticationMethod>::iterator it = methods_.begin(); |
205 it != methods_.end(); ++it) { | 185 it != methods_.end(); ++it) { |
206 if (it != methods_.begin()) | 186 if (it != methods_.begin()) |
207 supported_methods << kSupportedMethodsSeparator; | 187 supported_methods << kSupportedMethodsSeparator; |
208 supported_methods << it->ToString(); | 188 supported_methods << it->ToString(); |
209 } | 189 } |
210 result->AddAttr(kSupportedMethodsAttributeQName, supported_methods.str()); | 190 result->AddAttr(kSupportedMethodsAttributeQName, supported_methods.str()); |
| 191 state_ = WAITING_MESSAGE; |
| 192 } else { |
| 193 if (current_authenticator_->state() == MESSAGE_READY) { |
| 194 result = current_authenticator_->GetNextMessage(); |
| 195 } else { |
| 196 result = CreateEmptyAuthenticatorMessage(); |
| 197 } |
| 198 state_ = current_authenticator_->state(); |
| 199 DCHECK(state_ == ACCEPTED || state_ == WAITING_MESSAGE); |
| 200 result->AddAttr(kMethodAttributeQName, current_method_.ToString()); |
211 } | 201 } |
212 | 202 |
213 return result.Pass(); | 203 return result.Pass(); |
214 } | 204 } |
215 | 205 |
216 void NegotiatingAuthenticator::AddMethod(const AuthenticationMethod& method) { | 206 void NegotiatingAuthenticator::AddMethod(const AuthenticationMethod& method) { |
217 DCHECK(method.is_valid()); | 207 DCHECK(method.is_valid()); |
218 methods_.push_back(method); | 208 methods_.push_back(method); |
219 } | 209 } |
220 | 210 |
221 scoped_ptr<ChannelAuthenticator> | 211 scoped_ptr<ChannelAuthenticator> |
222 NegotiatingAuthenticator::CreateChannelAuthenticator() const { | 212 NegotiatingAuthenticator::CreateChannelAuthenticator() const { |
223 DCHECK_EQ(state(), ACCEPTED); | 213 DCHECK_EQ(state(), ACCEPTED); |
224 return current_authenticator_->CreateChannelAuthenticator(); | 214 return current_authenticator_->CreateChannelAuthenticator(); |
225 } | 215 } |
226 | 216 |
227 bool NegotiatingAuthenticator::is_host_side() const { | 217 bool NegotiatingAuthenticator::is_host_side() const { |
228 return local_key_pair_.get() != NULL; | 218 return local_key_pair_.get() != NULL; |
229 } | 219 } |
230 | 220 |
231 void NegotiatingAuthenticator::CreateAuthenticator(State initial_state) { | 221 void NegotiatingAuthenticator::CreateAuthenticator( |
| 222 Authenticator::State preferred_initial_state, |
| 223 const base::Closure& resume_callback) { |
232 if (is_host_side()) { | 224 if (is_host_side()) { |
233 current_authenticator_ = V2Authenticator::CreateForHost( | 225 current_authenticator_ = V2Authenticator::CreateForHost( |
234 local_cert_, local_key_pair_, shared_secret_hash_, initial_state); | 226 local_cert_, local_key_pair_, shared_secret_hash_, |
| 227 preferred_initial_state); |
| 228 resume_callback.Run(); |
235 } else { | 229 } else { |
236 current_authenticator_ = V2Authenticator::CreateForClient( | 230 fetch_secret_callback_.Run(base::Bind( |
237 AuthenticationMethod::ApplyHashFunction( | 231 &NegotiatingAuthenticator::CreateV2AuthenticatorWithSecret, |
238 current_method_.hash_function(), | 232 weak_factory_.GetWeakPtr(), preferred_initial_state, resume_callback)); |
239 authentication_tag_, shared_secret_), initial_state); | |
240 } | 233 } |
241 } | 234 } |
242 | 235 |
| 236 void NegotiatingAuthenticator::ProcessMessageInternal( |
| 237 const buzz::XmlElement* message, |
| 238 const base::Closure& resume_callback) { |
| 239 if (current_authenticator_->state() == WAITING_MESSAGE) { |
| 240 // If the message was not discarded and the authenticator is waiting for it, |
| 241 // give it to the underlying authenticator to process. |
| 242 // |current_authenticator_| is owned, so Unretained() is safe here. |
| 243 current_authenticator_->ProcessMessage(message, base::Bind( |
| 244 &NegotiatingAuthenticator::UpdateState, |
| 245 base::Unretained(this), resume_callback)); |
| 246 } |
| 247 } |
| 248 |
| 249 void NegotiatingAuthenticator::CreateV2AuthenticatorWithSecret( |
| 250 Authenticator::State initial_state, |
| 251 const base::Closure& resume_callback, |
| 252 const std::string& shared_secret) { |
| 253 current_authenticator_ = V2Authenticator::CreateForClient( |
| 254 AuthenticationMethod::ApplyHashFunction( |
| 255 current_method_.hash_function(), authentication_tag_, shared_secret), |
| 256 initial_state); |
| 257 resume_callback.Run(); |
| 258 } |
| 259 |
243 } // namespace protocol | 260 } // namespace protocol |
244 } // namespace remoting | 261 } // namespace remoting |
OLD | NEW |