Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Unified Diff: remoting/protocol/third_party_authenticator.cc

Issue 12326090: Third Party authentication protocol. (Closed) Base URL: http://git.chromium.org/chromium/src.git@host_key_pair
Patch Set: Add the missing new files Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698