| 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 |