OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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/host/url_fetcher_token_validator_factory.h" |
| 6 |
| 7 #include <set> |
| 8 |
| 9 #include "base/base64.h" |
| 10 #include "base/bind.h" |
| 11 #include "base/callback.h" |
| 12 #include "base/json/json_reader.h" |
| 13 #include "base/location.h" |
| 14 #include "base/logging.h" |
| 15 #include "base/single_thread_task_runner.h" |
| 16 #include "base/values.h" |
| 17 #include "crypto/random.h" |
| 18 #include "googleurl/src/gurl.h" |
| 19 #include "net/base/escape.h" |
| 20 #include "net/url_request/url_fetcher.h" |
| 21 #include "net/url_request/url_fetcher_delegate.h" |
| 22 #include "net/url_request/url_request_status.h" |
| 23 #include "remoting/base/rsa_key_pair.h" |
| 24 |
| 25 namespace { |
| 26 |
| 27 // Length in bytes of the cryptographic nonce used to salt the token scope. |
| 28 const size_t kNonceLength = 16; // 128 bits. |
| 29 |
| 30 } |
| 31 |
| 32 namespace remoting { |
| 33 |
| 34 class UrlFetcherTokenValidator |
| 35 : public net::URLFetcherDelegate, |
| 36 public protocol::ThirdPartyHostAuthenticator::TokenValidator { |
| 37 public: |
| 38 UrlFetcherTokenValidator( |
| 39 const GURL& token_url, |
| 40 const GURL& token_validation_url, |
| 41 scoped_refptr<RsaKeyPair> key_pair, |
| 42 const std::string& local_jid, |
| 43 const std::string& remote_jid, |
| 44 scoped_refptr<net::URLRequestContextGetter> request_context_getter) |
| 45 : token_url_(token_url), |
| 46 token_validation_url_(token_validation_url), |
| 47 key_pair_(key_pair), |
| 48 request_context_getter_(request_context_getter) { |
| 49 DCHECK(token_url_.is_valid()); |
| 50 DCHECK(token_validation_url_.is_valid()); |
| 51 DCHECK(key_pair_); |
| 52 token_scope_ = CreateScope(local_jid, remote_jid); |
| 53 } |
| 54 |
| 55 virtual ~UrlFetcherTokenValidator() { |
| 56 } |
| 57 |
| 58 // TokenValidator interface. |
| 59 virtual void ValidateThirdPartyToken( |
| 60 const std::string& token, |
| 61 const base::Callback<void( |
| 62 const std::string& shared_secret)>& on_token_validated) OVERRIDE { |
| 63 DCHECK(!request_); |
| 64 DCHECK(!on_token_validated.is_null()); |
| 65 |
| 66 on_token_validated_ = on_token_validated; |
| 67 |
| 68 std::string post_body = |
| 69 "code=" + net::EscapeUrlEncodedData(token, true) + |
| 70 "&client_id=" + net::EscapeUrlEncodedData( |
| 71 key_pair_->GetPublicKey(), true) + |
| 72 "&client_secret=" + net::EscapeUrlEncodedData( |
| 73 key_pair_->SignMessage(token), true) + |
| 74 "&grant_type=authorization_code"; |
| 75 request_.reset(net::URLFetcher::Create( |
| 76 token_validation_url_, net::URLFetcher::POST, this)); |
| 77 request_->SetUploadData("application/x-www-form-urlencoded", post_body); |
| 78 request_->SetRequestContext(request_context_getter_); |
| 79 request_->Start(); |
| 80 } |
| 81 |
| 82 virtual const GURL& token_url() const OVERRIDE { |
| 83 return token_url_; |
| 84 } |
| 85 |
| 86 virtual const std::string& token_scope() const OVERRIDE { |
| 87 return token_scope_; |
| 88 } |
| 89 |
| 90 // URLFetcherDelegate interface. |
| 91 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { |
| 92 DCHECK_EQ(request_.get(), source); |
| 93 std::string shared_token = GetSharedSecretFromResponse(source); |
| 94 on_token_validated_.Run(shared_token); |
| 95 request_.reset(); |
| 96 } |
| 97 |
| 98 private: |
| 99 bool IsValidScope(const std::string& token_scope) { |
| 100 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc. |
| 101 return token_scope == token_scope_; |
| 102 } |
| 103 |
| 104 static std::string CreateScope(const std::string& local_jid, |
| 105 const std::string& remote_jid) { |
| 106 char nonce_bytes[kNonceLength]; |
| 107 crypto::RandBytes(nonce_bytes, kNonceLength); |
| 108 std::string nonce; |
| 109 bool success = base::Base64Encode(nonce_bytes, &nonce); |
| 110 DCHECK(success); |
| 111 return "client:" + remote_jid + " host:" + local_jid + " nonce:" + nonce; |
| 112 } |
| 113 |
| 114 std::string GetSharedSecretFromResponse(const net::URLFetcher* source) { |
| 115 std::string shared_secret; |
| 116 |
| 117 // Verify that we got a successful response. |
| 118 int response = source->GetResponseCode(); |
| 119 net::URLRequestStatus status = source->GetStatus(); |
| 120 std::string data; |
| 121 if (!status.is_success() || response != 200) { |
| 122 LOG(ERROR) << |
| 123 "Error " << response << " validating token: '" << data << "'"; |
| 124 return shared_secret; |
| 125 } |
| 126 |
| 127 // Decode the JSON data from the response. |
| 128 source->GetResponseAsString(&data); |
| 129 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); |
| 130 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) { |
| 131 LOG(ERROR) << "Invalid token validation response: '" << data << "'"; |
| 132 return shared_secret; |
| 133 } |
| 134 |
| 135 // Get the scope from the response, and verify that it is valid. |
| 136 DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); |
| 137 std::string token_scope; |
| 138 dict->GetStringWithoutPathExpansion("scope", &token_scope); |
| 139 if (!IsValidScope(token_scope)) { |
| 140 LOG(ERROR) << "Invalid scope: '" << token_scope |
| 141 << "', expected: '" << token_scope_ <<"'."; |
| 142 return shared_secret; |
| 143 } |
| 144 |
| 145 // Everything is valid, so return the shared secret to the caller. |
| 146 dict->GetStringWithoutPathExpansion("access_token", &shared_secret); |
| 147 return shared_secret; |
| 148 } |
| 149 |
| 150 scoped_ptr<net::URLFetcher> request_; |
| 151 GURL token_url_; |
| 152 GURL token_validation_url_; |
| 153 scoped_refptr<RsaKeyPair> key_pair_; |
| 154 std::string token_scope_; |
| 155 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| 156 base::Callback<void(const std::string& shared_secret)> on_token_validated_; |
| 157 |
| 158 DISALLOW_COPY_AND_ASSIGN(UrlFetcherTokenValidator); |
| 159 }; |
| 160 |
| 161 UrlFetcherTokenValidatorFactory::UrlFetcherTokenValidatorFactory( |
| 162 const GURL& token_url, |
| 163 const GURL& token_validation_url, |
| 164 scoped_refptr<RsaKeyPair> key_pair, |
| 165 scoped_refptr<net::URLRequestContextGetter> request_context_getter) |
| 166 : token_url_(token_url), |
| 167 token_validation_url_(token_validation_url), |
| 168 key_pair_(key_pair), |
| 169 request_context_getter_(request_context_getter) { |
| 170 } |
| 171 |
| 172 UrlFetcherTokenValidatorFactory::~UrlFetcherTokenValidatorFactory() { |
| 173 } |
| 174 |
| 175 scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator> |
| 176 UrlFetcherTokenValidatorFactory::CreateTokenValidator( |
| 177 const std::string& local_jid, const std::string& remote_jid) { |
| 178 DCHECK(is_enabled()); |
| 179 return scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator>( |
| 180 new UrlFetcherTokenValidator(token_url_, token_validation_url_, key_pair_, |
| 181 local_jid, remote_jid, request_context_getter_)); |
| 182 } |
| 183 |
| 184 // Returns true if third party authentication is enabled for this host. |
| 185 bool UrlFetcherTokenValidatorFactory::is_enabled() const { |
| 186 return token_url_.is_valid() && token_validation_url_.is_valid() && key_pair_; |
| 187 } |
| 188 |
| 189 } // namespace remoting |
OLD | NEW |