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 35 namespace { 36 37 const int kBufferSize = 4096; 38 const char kCertIssuerWildCard[] = "*"; 39 40 } // namespace 41 42 namespace remoting { 43 44 TokenValidatorBase::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 57 TokenValidatorBase::~TokenValidatorBase() { 58 } 59 60 // TokenValidator interface. 61 void 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 73 const GURL& TokenValidatorBase::token_url() const { 74 return third_party_auth_config_.token_url; 75 } 76 77 const std::string& TokenValidatorBase::token_scope() const { 78 return token_scope_; 79 } 80 81 // URLFetcherDelegate interface. 82 void 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 90 void 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 110 void 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 140 void 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 157 bool 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 162 std::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