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 #else 124 #error Unknown platform. 125 #endif 126 // The callback is uncancellable, and GetClientCert requires selected_certs 127 // and client_cert_store to stay alive until the callback is called. So we 128 // must give it a WeakPtr for |this|, and ownership of the other parameters. 129 net::CertificateList* selected_certs(new net::CertificateList()); 130 client_cert_store->GetClientCerts( 131 *cert_request_info, selected_certs, 132 base::Bind(&TokenValidatorBase::OnCertificatesSelected, 133 weak_factory_.GetWeakPtr(), base::Owned(selected_certs), 134 base::Owned(client_cert_store))); 135 } 136 137 void TokenValidatorBase::OnCertificatesSelected( 138 net::CertificateList* selected_certs, 139 net::ClientCertStore* unused) { 140 const std::string& issuer = 141 third_party_auth_config_.token_validation_cert_issuer; 142 if (request_) { 143 for (size_t i = 0; i < selected_certs->size(); ++i) { 144 if (issuer == kCertIssuerWildCard || 145 issuer == (*selected_certs)[i]->issuer().common_name) { 146 request_->ContinueWithCertificate((*selected_certs)[i]); 147 return; 148 } 149 } 150 request_->ContinueWithCertificate(NULL); 151 } 152 } 153 154 bool TokenValidatorBase::IsValidScope(const std::string& token_scope) { 155 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc. 156 return token_scope == token_scope_; 157 } 158 159 std::string TokenValidatorBase::ProcessResponse() { 160 // Verify that we got a successful response. 161 net::URLRequestStatus status = request_->status(); 162 if (!status.is_success()) { 163 LOG(ERROR) << "Error validating token, status=" << status.status() 164 << " err=" << status.error(); 165 return std::string(); 166 } 167 168 int response = request_->GetResponseCode(); 169 if (response != 200) { 170 LOG(ERROR) 171 << "Error " << response << " validating token: '" << data_ << "'"; 172 return std::string(); 173 } 174 175 // Decode the JSON data from the response. 176 scoped_ptr<base::Value> value(base::JSONReader::Read(data_)); 177 base::DictionaryValue* dict; 178 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY || 179 !value->GetAsDictionary(&dict)) { 180 LOG(ERROR) << "Invalid token validation response: '" << data_ << "'"; 181 return std::string(); 182 } 183 184 std::string token_scope; 185 dict->GetStringWithoutPathExpansion("scope", &token_scope); 186 if (!IsValidScope(token_scope)) { 187 LOG(ERROR) << "Invalid scope: '" << token_scope 188 << "', expected: '" << token_scope_ <<"'."; 189 return std::string(); 190 } 191 192 std::string shared_secret; 193 // Everything is valid, so return the shared secret to the caller. 194 dict->GetStringWithoutPathExpansion("access_token", &shared_secret); 195 return shared_secret; 196 } 197 198 } // namespace remoting 199