Chromium Code Reviews| Index: remoting/protocol/third_party_authenticator.cc |
| diff --git a/remoting/protocol/third_party_authenticator.cc b/remoting/protocol/third_party_authenticator.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..99f9ce42f57827e13227dc33691aebe36211bfbd |
| --- /dev/null |
| +++ b/remoting/protocol/third_party_authenticator.cc |
| @@ -0,0 +1,279 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "remoting/protocol/third_party_authenticator.h" |
| + |
| +#include "base/base64.h" |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/logging.h" |
| +#include "crypto/rsa_private_key.h" |
| +#include "remoting/base/constants.h" |
| +#include "remoting/protocol/channel_authenticator.h" |
| +#include "remoting/protocol/v2_authenticator.h" |
| +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" |
| + |
| +#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.
|
| +#undef GetMessage |
| +#endif |
| + |
| +namespace { |
| + |
| +const buzz::StaticQName kTokenUrlTag = { remoting::kChromotingXmlNamespace, |
| + "third-party-token-url" }; |
| +const buzz::StaticQName kTokenScopeTag = { remoting::kChromotingXmlNamespace, |
| + "third-party-token-scope" }; |
| +const buzz::StaticQName kTokenTag = { remoting::kChromotingXmlNamespace, |
| + "third-party-token" }; |
| +} // namespace |
| + |
|
Sergey Ulanov
2013/02/26 01:14:50
remove extra empty line.
rmsousa
2013/03/05 03:30:24
Done.
|
| + |
| +namespace remoting { |
| +namespace protocol { |
| + |
| +// static |
| +scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForClient( |
| + const std::string& host_public_key, |
| + ThirdPartyAuthenticator::TokenFetcher* token_fetcher, |
| + Authenticator::State initial_state) { |
| + scoped_ptr<ThirdPartyAuthenticator> result(new ThirdPartyAuthenticator( |
| + initial_state)); |
| + result->host_public_key_ = host_public_key; |
| + result->token_fetcher_ = token_fetcher; |
| + return scoped_ptr<Authenticator>(result.Pass()); |
| +} |
| +// static |
|
Wez
2013/02/27 07:05:30
nit: Blank line, plz
rmsousa
2013/03/05 03:30:24
Done.
|
| +scoped_ptr<Authenticator> ThirdPartyAuthenticator::CreateForHost( |
| + const std::string& local_cert, |
| + scoped_ptr<KeyPair> key_pair, |
| + const std::string& token_url, |
| + const std::string& token_validation_url, |
| + const std::string& token_scope, |
| + scoped_ptr<ThirdPartyAuthenticator::TokenValidator> token_validator, |
| + Authenticator::State initial_state) { |
| + scoped_ptr<ThirdPartyAuthenticator> result(new ThirdPartyAuthenticator( |
| + initial_state)); |
| + result->local_cert_ = local_cert; |
| + result->key_pair_ = key_pair.Pass(); |
| + result->host_public_key_ = result->key_pair_->GetPublicKey(); |
| + result->token_url_ = token_url; |
| + result->token_validation_url_ = token_validation_url; |
| + result->token_scope_ = token_scope; |
| + result->token_validator_ = token_validator.Pass(); |
| + 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.
|
| + return scoped_ptr<Authenticator>(result.Pass()); |
| +} |
| + |
| +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
|
| + Authenticator::State initial_state) |
| + : expecting_token_(false), |
| + token_validator_(NULL), |
| + token_fetcher_(NULL), |
| + state_(initial_state), |
| + rejection_reason_(INVALID_CREDENTIALS), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
| +} |
| + |
| +ThirdPartyAuthenticator::~ThirdPartyAuthenticator() { |
| +} |
| + |
| +Authenticator::State ThirdPartyAuthenticator::state() const { |
| + if (state_ == ACCEPTED) { |
|
Wez
2013/02/27 07:05:30
nit: No need for {} for simple single-line if.
|
| + return underlying_->state(); |
| + } |
| + return state_; |
| +} |
| + |
| +Authenticator::RejectionReason ThirdPartyAuthenticator::rejection_reason() |
| + const { |
| + DCHECK_EQ(state(), REJECTED); |
| + if (state_ == REJECTED) { |
| + return rejection_reason_; |
| + } else { |
| + return underlying_->rejection_reason(); |
| + } |
| +} |
| + |
| +void ThirdPartyAuthenticator::ProcessMessage(const buzz::XmlElement* message) { |
| + DCHECK_EQ(state(), WAITING_MESSAGE); |
| + |
| + // The other side may have started the SPAKE negotiation. |
| + // Keep a copy of the message until the V2 authenticator is ready. |
| + const buzz::XmlElement* underlying_message = |
| + Authenticator::FindAuthenticatorMessage(message); |
| + if (underlying_message) { |
| + if (underlying_) { |
| + // Pass the message through the underlying authenticator. |
| + DCHECK_EQ(underlying_->state(), WAITING_MESSAGE); |
| + underlying_->ProcessMessage(underlying_message); |
| + } else { |
| + pending_message_.reset(new buzz::XmlElement(*underlying_message)); |
| + } |
| + } |
| + |
| + 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
|
| + if (!is_host_side()) { |
| + if (token_url_.empty()) { |
| + // 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
|
| + DCHECK(token_scope_.empty()); |
| + token_url_ = message->TextNamed(kTokenUrlTag); |
| + token_scope_ = message->TextNamed(kTokenScopeTag); |
| + if (!token_url_.empty() && !token_scope_.empty()) { |
| + state_ = WAITING_EXTERNAL; |
| + return; |
| + } |
| + } |
| + LOG(WARNING) << "Missing token issue URL/verification URL/scope."; |
| + } else { |
| + 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.
|
| + // 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.
|
| + // first message and send the URLs to the client. |
| + state_ = MESSAGE_READY; |
| + return; |
| + } |
| + DCHECK(token_.empty()); |
| + DCHECK(token_signature_.empty()); |
| + // Host has already sent the URL and expects a token from the client. |
| + token_ = message->TextNamed(kTokenTag); |
| + token_signature_ = key_pair_->GetSignature(token_); |
| + if (!token_.empty() && !token_signature_.empty()) { |
| + state_ = WAITING_EXTERNAL; |
| + return; |
| + } |
| + LOG(WARNING) << "Missing token."; |
| + } |
| + state_ = REJECTED; |
| + rejection_reason_ = PROTOCOL_ERROR; |
| + return; |
| + } |
| +} |
| + |
| +scoped_ptr<buzz::XmlElement> ThirdPartyAuthenticator::GetNextMessage() { |
| + DCHECK_EQ(state(), MESSAGE_READY); |
| + |
| + scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage(); |
| + if (state_ == MESSAGE_READY) { |
|
Sergey Ulanov
2013/02/26 01:14:50
same here - there is a DCHECK for this condition.
|
| + if (!is_host_side()) { |
| + if (!token_.empty()) { |
| + buzz::XmlElement* token_tag = new buzz::XmlElement(kTokenTag); |
| + token_tag->SetBodyText(token_); |
| + message->AddElement(token_tag); |
| + } else { |
| + // The client doesn't really have anything to send yet, it's just |
| + // 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
|
| + } |
| + } else { |
| + if (token_.empty()) { |
| + DCHECK(!token_url_.empty()); |
| + DCHECK(!token_scope_.empty()); |
| + buzz::XmlElement* token_url_tag = new buzz::XmlElement( |
| + kTokenUrlTag); |
| + token_url_tag->SetBodyText(token_url_); |
| + message->AddElement(token_url_tag); |
| + buzz::XmlElement* token_scope_tag = new buzz::XmlElement( |
| + kTokenScopeTag); |
| + token_scope_tag->SetBodyText(token_scope_); |
| + message->AddElement(token_scope_tag); |
| + expecting_token_ = true; |
| + } |
| + } |
| + } |
| + 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.
|
| + DCHECK(!shared_secret_.empty()); |
| + // This side already has the shared secret and can start the SPAKE |
| + // negotiation in parallel with the remaining token messages. |
| + message->AddElement(underlying_->GetNextMessage().release()); |
| + state_ = ACCEPTED; |
| + } else { |
| + state_ = WAITING_MESSAGE; |
| + } |
| + return message.Pass(); |
| +} |
| + |
| +scoped_ptr<ChannelAuthenticator> |
| +ThirdPartyAuthenticator::CreateChannelAuthenticator() const { |
| + DCHECK_EQ(state(), ACCEPTED); |
| + return underlying_->CreateChannelAuthenticator(); |
| +} |
| + |
| +bool ThirdPartyAuthenticator::is_host_side() const { |
| + return !token_fetcher_; |
| +} |
| + |
| +void ThirdPartyAuthenticator::PerformExternalAction( |
| + const base::Closure& resume_callback) { |
| + DCHECK_EQ(state(), WAITING_EXTERNAL); |
| + 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.
|
| + DCHECK(!token_signature_.empty()); |
| + DCHECK(!token_validation_url_.empty()); |
| + DCHECK(!token_.empty()); |
| + DCHECK(!host_public_key_.empty()); |
| + DCHECK(!token_scope_.empty()); |
| + token_validator_->ValidateThirdPartyToken( |
| + token_validation_url_, token_, host_public_key_, token_signature_, |
| + token_scope_, base::Bind( |
| + &ThirdPartyAuthenticator::OnThirdPartyTokenValidated, |
| + weak_factory_.GetWeakPtr(), |
| + resume_callback)); |
| + } else { |
| + DCHECK(token_.empty()); |
| + token_fetcher_->FetchThirdPartyToken( |
| + token_url_, host_public_key_, token_scope_, base::Bind( |
| + &ThirdPartyAuthenticator::OnThirdPartyTokenFetched, |
| + weak_factory_.GetWeakPtr(), |
| + resume_callback)); |
| + } |
| +} |
| + |
| +void ThirdPartyAuthenticator::OnThirdPartyTokenFetched( |
| + 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.
|
| + const std::string& shared_secret) { |
| + DCHECK_EQ(state_, WAITING_EXTERNAL); |
| + DCHECK(!is_host_side()); |
| + token_ = third_party_token; |
| + if (!token_.empty() && !shared_secret.empty()) { |
| + this->OnThirdPartyTokenValidated(resume_callback, shared_secret); |
| + return; |
| + } |
| + state_ = REJECTED; |
| + rejection_reason_ = INVALID_CREDENTIALS; |
| + resume_callback.Run(); |
| +} |
| + |
| +void ThirdPartyAuthenticator::OnThirdPartyTokenValidated( |
| + 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.
|
| + DCHECK_EQ(state_, WAITING_EXTERNAL); |
| + shared_secret_ = shared_secret; |
| + Authenticator::State initial_state; |
| + if (!shared_secret_.empty()) { |
| + if (pending_message_) { |
| + // The other side already started the SPAKE authentication. |
| + state_ = ACCEPTED; |
| + initial_state = WAITING_MESSAGE; |
| + } else { |
| + // The other side still needs data from this side to start SPAKE. |
| + state_ = MESSAGE_READY; |
| + initial_state = MESSAGE_READY; |
| + } |
| + if (is_host_side()) { |
| + underlying_ = V2Authenticator::CreateForHost( |
| + local_cert_, key_pair_->Copy(), shared_secret_, |
| + initial_state); |
| + } else { |
| + underlying_ = V2Authenticator::CreateForClient( |
| + shared_secret_, initial_state); |
| + } |
| + if (pending_message_) { |
| + underlying_->ProcessMessage(pending_message_.release()); |
| + DCHECK_NE(underlying_->state(), WAITING_MESSAGE); |
| + } |
| + } else { |
| + state_ = REJECTED; |
| + rejection_reason_ = INVALID_CREDENTIALS; |
| + } |
| + resume_callback.Run(); |
| +} |
| + |
| +} // namespace protocol |
| +} // namespace remoting |