Home | History | Annotate | Download | only in host
      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