Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "remoting/protocol/third_party_authenticator.h" | |
| 6 | |
| 7 #include "base/base64.h" | |
| 8 #include "base/bind.h" | |
| 9 #include "base/callback.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "crypto/rsa_private_key.h" | |
| 12 #include "remoting/base/constants.h" | |
| 13 #include "remoting/protocol/channel_authenticator.h" | |
| 14 #include "remoting/protocol/v2_authenticator.h" | |
| 15 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" | |
| 16 | |
| 17 #if defined(_WIN32) && defined(GetMessage) | |
|
Sergey Ulanov
2013/02/26 01:14:50
Do you need this here?
rmsousa
2013/03/05 03:30:24
Done.
| |
| 18 #undef GetMessage | |
| 19 #endif | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 const buzz::StaticQName kTokenUrlTag = { remoting::kChromotingXmlNamespace, | |
| 24 "third-party-token-url" }; | |
| 25 const buzz::StaticQName kTokenScopeTag = { remoting::kChromotingXmlNamespace, | |
| 26 "third-party-token-scope" }; | |
| 27 const buzz::StaticQName kTokenTag = { remoting::kChromotingXmlNamespace, | |
| 28 "third-party-token" }; | |
| 29 } // namespace | |
| 30 | |
|
Sergey Ulanov
2013/02/26 01:14:50
remove extra empty line.
rmsousa
2013/03/05 03:30:24
Done.
| |
| 31 | |
| 32 namespace remoting { | |
| 33 namespace protocol { | |
| 34 | |
| 35 // static | |
| 36 scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForClient( | |
| 37 const std::string& host_public_key, | |
| 38 ThirdPartyAuthenticator::TokenFetcher* token_fetcher, | |
| 39 Authenticator::State initial_state) { | |
| 40 scoped_ptr<ThirdPartyAuthenticator> result(new ThirdPartyAuthenticator( | |
| 41 initial_state)); | |
| 42 result->host_public_key_ = host_public_key; | |
| 43 result->token_fetcher_ = token_fetcher; | |
| 44 return scoped_ptr<Authenticator>(result.Pass()); | |
| 45 } | |
| 46 // static | |
|
Wez
2013/02/27 07:05:30
nit: Blank line, plz
rmsousa
2013/03/05 03:30:24
Done.
| |
| 47 scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForHost( | |
| 48 const std::string& local_cert, | |
| 49 scoped_ptr<KeyPair> key_pair, | |
| 50 const std::string& token_url, | |
| 51 const std::string& token_validation_url, | |
| 52 const std::string& token_scope, | |
| 53 scoped_ptr<ThirdPartyAuthenticator::TokenValidator> token_validator, | |
| 54 Authenticator::State initial_state) { | |
| 55 scoped_ptr<ThirdPartyAuthenticator> result(new ThirdPartyAuthenticator( | |
| 56 initial_state)); | |
| 57 result->local_cert_ = local_cert; | |
| 58 result->key_pair_ = key_pair.Pass(); | |
| 59 result->host_public_key_ = result->key_pair_->GetPublicKey(); | |
| 60 result->token_url_ = token_url; | |
| 61 result->token_validation_url_ = token_validation_url; | |
| 62 result->token_scope_ = token_scope; | |
| 63 result->token_validator_ = token_validator.Pass(); | |
| 64 result->expecting_token_ = false; | |
|
Sergey Ulanov
2013/02/26 01:14:50
don't need this - it should be already initialized
rmsousa
2013/03/05 03:30:24
Done.
| |
| 65 return scoped_ptr<Authenticator>(result.Pass()); | |
| 66 } | |
| 67 | |
| 68 ThirdPartyAuthenticator::ThirdPartyAuthenticator( | |
|
Sergey Ulanov
2013/02/26 01:14:50
Would it make sense two have separate classes - on
Wez
2013/02/27 07:05:30
Agreed.
You don't really need a public class for
rmsousa
2013/03/05 03:30:24
Done.
rmsousa
2013/03/05 03:30:24
I separated in one public class plus two local sub
| |
| 69 Authenticator::State initial_state) | |
| 70 : expecting_token_(false), | |
| 71 token_validator_(NULL), | |
| 72 token_fetcher_(NULL), | |
| 73 state_(initial_state), | |
| 74 rejection_reason_(INVALID_CREDENTIALS), | |
| 75 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
| 76 } | |
| 77 | |
| 78 ThirdPartyAuthenticator::~ThirdPartyAuthenticator() { | |
| 79 } | |
| 80 | |
| 81 Authenticator::State ThirdPartyAuthenticator::state() const { | |
| 82 if (state_ == ACCEPTED) { | |
|
Wez
2013/02/27 07:05:30
nit: No need for {} for simple single-line if.
| |
| 83 return underlying_->state(); | |
| 84 } | |
| 85 return state_; | |
| 86 } | |
| 87 | |
| 88 Authenticator::RejectionReason ThirdPartyAuthenticator::rejection_reason() | |
| 89 const { | |
| 90 DCHECK_EQ(state(), REJECTED); | |
| 91 if (state_ == REJECTED) { | |
| 92 return rejection_reason_; | |
| 93 } else { | |
| 94 return underlying_->rejection_reason(); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 void ThirdPartyAuthenticator::ProcessMessage(const buzz::XmlElement* message) { | |
| 99 DCHECK_EQ(state(), WAITING_MESSAGE); | |
| 100 | |
| 101 // The other side may have started the SPAKE negotiation. | |
| 102 // Keep a copy of the message until the V2 authenticator is ready. | |
| 103 const buzz::XmlElement* underlying_message = | |
| 104 Authenticator::FindAuthenticatorMessage(message); | |
| 105 if (underlying_message) { | |
| 106 if (underlying_) { | |
| 107 // Pass the message through the underlying authenticator. | |
| 108 DCHECK_EQ(underlying_->state(), WAITING_MESSAGE); | |
| 109 underlying_->ProcessMessage(underlying_message); | |
| 110 } else { | |
| 111 pending_message_.reset(new buzz::XmlElement(*underlying_message)); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 if (state_ == WAITING_MESSAGE) { | |
|
Sergey Ulanov
2013/02/26 01:14:50
there is a DCHECK for this condition at the top, s
Wez
2013/02/27 07:05:30
Is the caller responsible for never calling Proces
rmsousa
2013/03/05 03:30:24
the DCHECK is for state(), which is the authentica
rmsousa
2013/03/05 03:30:24
The caller is responsible for not calling if state
| |
| 116 if (!is_host_side()) { | |
| 117 if (token_url_.empty()) { | |
| 118 // First message from the host, token URL and scope. | |
|
Wez
2013/02/27 07:05:30
nit: Make the above a sentence. Also, please move
| |
| 119 DCHECK(token_scope_.empty()); | |
| 120 token_url_ = message->TextNamed(kTokenUrlTag); | |
| 121 token_scope_ = message->TextNamed(kTokenScopeTag); | |
| 122 if (!token_url_.empty() && !token_scope_.empty()) { | |
| 123 state_ = WAITING_EXTERNAL; | |
| 124 return; | |
| 125 } | |
| 126 } | |
| 127 LOG(WARNING) << "Missing token issue URL/verification URL/scope."; | |
| 128 } else { | |
| 129 if (!expecting_token_) { | |
|
Wez
2013/02/27 07:05:30
Should this be |has_sent_urls| instead?
rmsousa
2013/03/05 03:30:24
Done.
| |
| 130 // The host hasn't send the token URLs to the client yet, so ignore the | |
|
Wez
2013/02/27 07:05:30
typo: send -> sent
rmsousa
2013/03/05 03:30:24
Done.
| |
| 131 // first message and send the URLs to the client. | |
| 132 state_ = MESSAGE_READY; | |
| 133 return; | |
| 134 } | |
| 135 DCHECK(token_.empty()); | |
| 136 DCHECK(token_signature_.empty()); | |
| 137 // Host has already sent the URL and expects a token from the client. | |
| 138 token_ = message->TextNamed(kTokenTag); | |
| 139 token_signature_ = key_pair_->GetSignature(token_); | |
| 140 if (!token_.empty() && !token_signature_.empty()) { | |
| 141 state_ = WAITING_EXTERNAL; | |
| 142 return; | |
| 143 } | |
| 144 LOG(WARNING) << "Missing token."; | |
| 145 } | |
| 146 state_ = REJECTED; | |
| 147 rejection_reason_ = PROTOCOL_ERROR; | |
| 148 return; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 scoped_ptr<buzz::XmlElement> ThirdPartyAuthenticator::GetNextMessage() { | |
| 153 DCHECK_EQ(state(), MESSAGE_READY); | |
| 154 | |
| 155 scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage(); | |
| 156 if (state_ == MESSAGE_READY) { | |
|
Sergey Ulanov
2013/02/26 01:14:50
same here - there is a DCHECK for this condition.
| |
| 157 if (!is_host_side()) { | |
| 158 if (!token_.empty()) { | |
| 159 buzz::XmlElement* token_tag = new buzz::XmlElement(kTokenTag); | |
| 160 token_tag->SetBodyText(token_); | |
| 161 message->AddElement(token_tag); | |
| 162 } else { | |
| 163 // The client doesn't really have anything to send yet, it's just | |
| 164 // waiting for the host to send the token_url. | |
|
Wez
2013/02/27 07:05:30
Surely the client should never be in the MESSAGE_R
rmsousa
2013/03/05 03:30:24
We expect an authentication message on the session
| |
| 165 } | |
| 166 } else { | |
| 167 if (token_.empty()) { | |
| 168 DCHECK(!token_url_.empty()); | |
| 169 DCHECK(!token_scope_.empty()); | |
| 170 buzz::XmlElement* token_url_tag = new buzz::XmlElement( | |
| 171 kTokenUrlTag); | |
| 172 token_url_tag->SetBodyText(token_url_); | |
| 173 message->AddElement(token_url_tag); | |
| 174 buzz::XmlElement* token_scope_tag = new buzz::XmlElement( | |
| 175 kTokenScopeTag); | |
| 176 token_scope_tag->SetBodyText(token_scope_); | |
| 177 message->AddElement(token_scope_tag); | |
| 178 expecting_token_ = true; | |
| 179 } | |
| 180 } | |
| 181 } | |
| 182 if (underlying_ && underlying_->state() == MESSAGE_READY) { | |
|
Wez
2013/02/27 07:05:30
nit: Blank lines before & after this if...else...
rmsousa
2013/03/05 03:30:24
Done.
| |
| 183 DCHECK(!shared_secret_.empty()); | |
| 184 // This side already has the shared secret and can start the SPAKE | |
| 185 // negotiation in parallel with the remaining token messages. | |
| 186 message->AddElement(underlying_->GetNextMessage().release()); | |
| 187 state_ = ACCEPTED; | |
| 188 } else { | |
| 189 state_ = WAITING_MESSAGE; | |
| 190 } | |
| 191 return message.Pass(); | |
| 192 } | |
| 193 | |
| 194 scoped_ptr<ChannelAuthenticator> | |
| 195 ThirdPartyAuthenticator::CreateChannelAuthenticator() const { | |
| 196 DCHECK_EQ(state(), ACCEPTED); | |
| 197 return underlying_->CreateChannelAuthenticator(); | |
| 198 } | |
| 199 | |
| 200 bool ThirdPartyAuthenticator::is_host_side() const { | |
| 201 return !token_fetcher_; | |
| 202 } | |
| 203 | |
| 204 void ThirdPartyAuthenticator::PerformExternalAction( | |
| 205 const base::Closure& resume_callback) { | |
| 206 DCHECK_EQ(state(), WAITING_EXTERNAL); | |
| 207 if (is_host_side()) { | |
|
Sergey Ulanov
2013/02/26 01:14:50
Here the order of client and host parts is reverse
rmsousa
2013/03/05 03:30:24
Done.
| |
| 208 DCHECK(!token_signature_.empty()); | |
| 209 DCHECK(!token_validation_url_.empty()); | |
| 210 DCHECK(!token_.empty()); | |
| 211 DCHECK(!host_public_key_.empty()); | |
| 212 DCHECK(!token_scope_.empty()); | |
| 213 token_validator_->ValidateThirdPartyToken( | |
| 214 token_validation_url_, token_, host_public_key_, token_signature_, | |
| 215 token_scope_, base::Bind( | |
| 216 &ThirdPartyAuthenticator::OnThirdPartyTokenValidated, | |
| 217 weak_factory_.GetWeakPtr(), | |
| 218 resume_callback)); | |
| 219 } else { | |
| 220 DCHECK(token_.empty()); | |
| 221 token_fetcher_->FetchThirdPartyToken( | |
| 222 token_url_, host_public_key_, token_scope_, base::Bind( | |
| 223 &ThirdPartyAuthenticator::OnThirdPartyTokenFetched, | |
| 224 weak_factory_.GetWeakPtr(), | |
| 225 resume_callback)); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 void ThirdPartyAuthenticator::OnThirdPartyTokenFetched( | |
| 230 const base::Closure& resume_callback, const std::string& third_party_token, | |
|
Sergey Ulanov
2013/02/26 01:14:50
nit: one argument per line please when whole funct
rmsousa
2013/03/05 03:30:24
Done.
| |
| 231 const std::string& shared_secret) { | |
| 232 DCHECK_EQ(state_, WAITING_EXTERNAL); | |
| 233 DCHECK(!is_host_side()); | |
| 234 token_ = third_party_token; | |
| 235 if (!token_.empty() && !shared_secret.empty()) { | |
| 236 this->OnThirdPartyTokenValidated(resume_callback, shared_secret); | |
| 237 return; | |
| 238 } | |
| 239 state_ = REJECTED; | |
| 240 rejection_reason_ = INVALID_CREDENTIALS; | |
| 241 resume_callback.Run(); | |
| 242 } | |
| 243 | |
| 244 void ThirdPartyAuthenticator::OnThirdPartyTokenValidated( | |
| 245 const base::Closure& resume_callback, const std::string& shared_secret) { | |
|
Sergey Ulanov
2013/02/26 01:14:50
one argument per line please
rmsousa
2013/03/05 03:30:24
Done.
| |
| 246 DCHECK_EQ(state_, WAITING_EXTERNAL); | |
| 247 shared_secret_ = shared_secret; | |
| 248 Authenticator::State initial_state; | |
| 249 if (!shared_secret_.empty()) { | |
| 250 if (pending_message_) { | |
| 251 // The other side already started the SPAKE authentication. | |
| 252 state_ = ACCEPTED; | |
| 253 initial_state = WAITING_MESSAGE; | |
| 254 } else { | |
| 255 // The other side still needs data from this side to start SPAKE. | |
| 256 state_ = MESSAGE_READY; | |
| 257 initial_state = MESSAGE_READY; | |
| 258 } | |
| 259 if (is_host_side()) { | |
| 260 underlying_ = V2Authenticator::CreateForHost( | |
| 261 local_cert_, key_pair_->Copy(), shared_secret_, | |
| 262 initial_state); | |
| 263 } else { | |
| 264 underlying_ = V2Authenticator::CreateForClient( | |
| 265 shared_secret_, initial_state); | |
| 266 } | |
| 267 if (pending_message_) { | |
| 268 underlying_->ProcessMessage(pending_message_.release()); | |
| 269 DCHECK_NE(underlying_->state(), WAITING_MESSAGE); | |
| 270 } | |
| 271 } else { | |
| 272 state_ = REJECTED; | |
| 273 rejection_reason_ = INVALID_CREDENTIALS; | |
| 274 } | |
| 275 resume_callback.Run(); | |
| 276 } | |
| 277 | |
| 278 } // namespace protocol | |
| 279 } // namespace remoting | |
| OLD | NEW |