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.
5 #include "remoting/host/token_validator_base.h"
7 #include "base/base64.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_CERTS)
23 #include "net/ssl/client_cert_store_nss.h"
25 #include "net/ssl/client_cert_store_win.h"
26 #elif defined(OS_MACOSX)
27 #include "net/ssl/client_cert_store_mac.h"
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"
37 const int kBufferSize
= 4096;
38 const char kCertIssuerWildCard
[] = "*";
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
)),
53 DCHECK(third_party_auth_config_
.token_url
.is_valid());
54 DCHECK(third_party_auth_config_
.token_validation_url
.is_valid());
57 TokenValidatorBase::~TokenValidatorBase() {
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
) {
66 DCHECK(!on_token_validated
.is_null());
68 on_token_validated_
= on_token_validated
;
70 StartValidateRequest(token
);
73 const GURL
& TokenValidatorBase::token_url() const {
74 return third_party_auth_config_
.token_url
;
77 const std::string
& TokenValidatorBase::token_scope() const {
81 // URLFetcherDelegate interface.
82 void TokenValidatorBase::OnResponseStarted(net::URLRequest
* source
) {
83 DCHECK_EQ(request_
.get(), source
);
86 request_
->Read(buffer_
.get(), kBufferSize
, &bytes_read
);
87 OnReadCompleted(request_
.get(), bytes_read
);
90 void TokenValidatorBase::OnReadCompleted(net::URLRequest
* source
,
92 DCHECK_EQ(request_
.get(), source
);
95 if (!request_
->status().is_success() || bytes_read
<= 0)
98 data_
.append(buffer_
->data(), bytes_read
);
99 } while (request_
->Read(buffer_
.get(), kBufferSize
, &bytes_read
));
101 const net::URLRequestStatus status
= request_
->status();
103 if (!status
.is_io_pending()) {
104 std::string shared_token
= ProcessResponse();
106 on_token_validated_
.Run(shared_token
);
110 void TokenValidatorBase::OnCertificateRequested(
111 net::URLRequest
* source
,
112 net::SSLCertRequestInfo
* cert_request_info
) {
113 DCHECK_EQ(request_
.get(), source
);
115 net::ClientCertStore
* client_cert_store
;
116 #if defined(USE_NSS_CERTS)
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
= nullptr;
127 #error Unknown platform.
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
)));
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
;
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());
153 request_
->ContinueWithCertificate(nullptr);
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_
;
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();
171 int response
= request_
->GetResponseCode();
172 if (response
!= 200) {
174 << "Error " << response
<< " validating token: '" << data_
<< "'";
175 return std::string();
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
|| !value
->GetAsDictionary(&dict
)) {
182 LOG(ERROR
) << "Invalid token validation response: '" << data_
<< "'";
183 return std::string();
186 std::string token_scope
;
187 dict
->GetStringWithoutPathExpansion("scope", &token_scope
);
188 if (!IsValidScope(token_scope
)) {
189 LOG(ERROR
) << "Invalid scope: '" << token_scope
190 << "', expected: '" << token_scope_
<<"'.";
191 return std::string();
194 std::string shared_secret
;
195 // Everything is valid, so return the shared secret to the caller.
196 dict
->GetStringWithoutPathExpansion("access_token", &shared_secret
);
197 return shared_secret
;
200 } // namespace remoting