1// Copyright 2014 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/token_validator_base.h" 6 7#include "base/base64.h" 8#include "base/bind.h" 9#include "base/callback.h" 10#include "base/json/json_reader.h" 11#include "base/logging.h" 12#include "base/memory/weak_ptr.h" 13#include "base/single_thread_task_runner.h" 14#include "base/strings/string_util.h" 15#include "base/values.h" 16#include "net/base/escape.h" 17#include "net/base/io_buffer.h" 18#include "net/base/request_priority.h" 19#include "net/base/upload_bytes_element_reader.h" 20#include "net/base/upload_data_stream.h" 21#include "net/ssl/client_cert_store.h" 22#if defined(USE_NSS) 23#include "net/ssl/client_cert_store_nss.h" 24#elif defined(OS_WIN) 25#include "net/ssl/client_cert_store_win.h" 26#elif defined(OS_MACOSX) 27#include "net/ssl/client_cert_store_mac.h" 28#endif 29#include "net/ssl/ssl_cert_request_info.h" 30#include "net/url_request/url_request.h" 31#include "net/url_request/url_request_context.h" 32#include "net/url_request/url_request_status.h" 33#include "url/gurl.h" 34 35namespace { 36 37const int kBufferSize = 4096; 38const char kCertIssuerWildCard[] = "*"; 39 40} // namespace 41 42namespace remoting { 43 44TokenValidatorBase::TokenValidatorBase( 45 const ThirdPartyAuthConfig& third_party_auth_config, 46 const std::string& token_scope, 47 scoped_refptr<net::URLRequestContextGetter> request_context_getter) 48 : third_party_auth_config_(third_party_auth_config), 49 token_scope_(token_scope), 50 request_context_getter_(request_context_getter), 51 buffer_(new net::IOBuffer(kBufferSize)), 52 weak_factory_(this) { 53 DCHECK(third_party_auth_config_.token_url.is_valid()); 54 DCHECK(third_party_auth_config_.token_validation_url.is_valid()); 55} 56 57TokenValidatorBase::~TokenValidatorBase() { 58} 59 60// TokenValidator interface. 61void TokenValidatorBase::ValidateThirdPartyToken( 62 const std::string& token, 63 const base::Callback<void( 64 const std::string& shared_secret)>& on_token_validated) { 65 DCHECK(!request_); 66 DCHECK(!on_token_validated.is_null()); 67 68 on_token_validated_ = on_token_validated; 69 70 StartValidateRequest(token); 71} 72 73const GURL& TokenValidatorBase::token_url() const { 74 return third_party_auth_config_.token_url; 75} 76 77const std::string& TokenValidatorBase::token_scope() const { 78 return token_scope_; 79} 80 81// URLFetcherDelegate interface. 82void TokenValidatorBase::OnResponseStarted(net::URLRequest* source) { 83 DCHECK_EQ(request_.get(), source); 84 85 int bytes_read = 0; 86 request_->Read(buffer_.get(), kBufferSize, &bytes_read); 87 OnReadCompleted(request_.get(), bytes_read); 88} 89 90void TokenValidatorBase::OnReadCompleted(net::URLRequest* source, 91 int bytes_read) { 92 DCHECK_EQ(request_.get(), source); 93 94 do { 95 if (!request_->status().is_success() || bytes_read <= 0) 96 break; 97 98 data_.append(buffer_->data(), bytes_read); 99 } while (request_->Read(buffer_.get(), kBufferSize, &bytes_read)); 100 101 const net::URLRequestStatus status = request_->status(); 102 103 if (!status.is_io_pending()) { 104 std::string shared_token = ProcessResponse(); 105 request_.reset(); 106 on_token_validated_.Run(shared_token); 107 } 108} 109 110void TokenValidatorBase::OnCertificateRequested( 111 net::URLRequest* source, 112 net::SSLCertRequestInfo* cert_request_info) { 113 DCHECK_EQ(request_.get(), source); 114 115 net::ClientCertStore* client_cert_store; 116#if defined(USE_NSS) 117 client_cert_store = new net::ClientCertStoreNSS( 118 net::ClientCertStoreNSS::PasswordDelegateFactory()); 119#elif defined(OS_WIN) 120 client_cert_store = new net::ClientCertStoreWin(); 121#elif defined(OS_MACOSX) 122 client_cert_store = new net::ClientCertStoreMac(); 123#elif defined(USE_OPENSSL) 124 // OpenSSL does not use the ClientCertStore infrastructure. 125 client_cert_store = NULL; 126#else 127#error Unknown platform. 128#endif 129 // The callback is uncancellable, and GetClientCert requires selected_certs 130 // and client_cert_store to stay alive until the callback is called. So we 131 // must give it a WeakPtr for |this|, and ownership of the other parameters. 132 net::CertificateList* selected_certs(new net::CertificateList()); 133 client_cert_store->GetClientCerts( 134 *cert_request_info, selected_certs, 135 base::Bind(&TokenValidatorBase::OnCertificatesSelected, 136 weak_factory_.GetWeakPtr(), base::Owned(selected_certs), 137 base::Owned(client_cert_store))); 138} 139 140void TokenValidatorBase::OnCertificatesSelected( 141 net::CertificateList* selected_certs, 142 net::ClientCertStore* unused) { 143 const std::string& issuer = 144 third_party_auth_config_.token_validation_cert_issuer; 145 if (request_) { 146 for (size_t i = 0; i < selected_certs->size(); ++i) { 147 if (issuer == kCertIssuerWildCard || 148 issuer == (*selected_certs)[i]->issuer().common_name) { 149 request_->ContinueWithCertificate((*selected_certs)[i].get()); 150 return; 151 } 152 } 153 request_->ContinueWithCertificate(NULL); 154 } 155} 156 157bool TokenValidatorBase::IsValidScope(const std::string& token_scope) { 158 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc. 159 return token_scope == token_scope_; 160} 161 162std::string TokenValidatorBase::ProcessResponse() { 163 // Verify that we got a successful response. 164 net::URLRequestStatus status = request_->status(); 165 if (!status.is_success()) { 166 LOG(ERROR) << "Error validating token, status=" << status.status() 167 << " err=" << status.error(); 168 return std::string(); 169 } 170 171 int response = request_->GetResponseCode(); 172 if (response != 200) { 173 LOG(ERROR) 174 << "Error " << response << " validating token: '" << data_ << "'"; 175 return std::string(); 176 } 177 178 // Decode the JSON data from the response. 179 scoped_ptr<base::Value> value(base::JSONReader::Read(data_)); 180 base::DictionaryValue* dict; 181 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY || 182 !value->GetAsDictionary(&dict)) { 183 LOG(ERROR) << "Invalid token validation response: '" << data_ << "'"; 184 return std::string(); 185 } 186 187 std::string token_scope; 188 dict->GetStringWithoutPathExpansion("scope", &token_scope); 189 if (!IsValidScope(token_scope)) { 190 LOG(ERROR) << "Invalid scope: '" << token_scope 191 << "', expected: '" << token_scope_ <<"'."; 192 return std::string(); 193 } 194 195 std::string shared_secret; 196 // Everything is valid, so return the shared secret to the caller. 197 dict->GetStringWithoutPathExpansion("access_token", &shared_secret); 198 return shared_secret; 199} 200 201} // namespace remoting 202