blob: 582b72b04a7566a9ceb4198458c01a3cdf917c4d [file] [log] [blame]
[email protected]d95ee262014-02-26 06:30:311// 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
avic5960f32015-12-22 22:49:487#include <stddef.h>
8
[email protected]d95ee262014-02-26 06:30:319#include "base/base64.h"
10#include "base/bind.h"
11#include "base/callback.h"
12#include "base/json/json_reader.h"
13#include "base/logging.h"
14#include "base/memory/weak_ptr.h"
15#include "base/single_thread_task_runner.h"
16#include "base/strings/string_util.h"
17#include "base/values.h"
avic5960f32015-12-22 22:49:4818#include "build/build_config.h"
[email protected]d95ee262014-02-26 06:30:3119#include "net/base/escape.h"
20#include "net/base/io_buffer.h"
21#include "net/base/request_priority.h"
22#include "net/base/upload_bytes_element_reader.h"
23#include "net/base/upload_data_stream.h"
24#include "net/ssl/client_cert_store.h"
davidben71f35ff2015-04-17 20:54:4825#if defined(USE_NSS_CERTS)
[email protected]d95ee262014-02-26 06:30:3126#include "net/ssl/client_cert_store_nss.h"
27#elif defined(OS_WIN)
28#include "net/ssl/client_cert_store_win.h"
29#elif defined(OS_MACOSX)
30#include "net/ssl/client_cert_store_mac.h"
31#endif
32#include "net/ssl/ssl_cert_request_info.h"
svaldez7872fd02015-11-19 21:10:5433#include "net/ssl/ssl_private_key.h"
lambroslambrouf43816ad2015-12-16 03:50:1434#include "net/url_request/redirect_info.h"
[email protected]d95ee262014-02-26 06:30:3135#include "net/url_request/url_request.h"
36#include "net/url_request/url_request_context.h"
37#include "net/url_request/url_request_status.h"
joedowd455f5372017-01-26 00:26:4038#include "remoting/base/logging.h"
[email protected]d95ee262014-02-26 06:30:3139#include "url/gurl.h"
40
41namespace {
42
Joe Downing78bfcca2018-11-07 23:43:0543constexpr int kBufferSize = 4096;
44constexpr char kCertIssuerWildCard[] = "*";
[email protected]d95ee262014-02-26 06:30:3145
Joe Downing78bfcca2018-11-07 23:43:0546// Returns a value from the issuer field for certificate selection, in order of
47// preference. If the O or OU entries are populated with multiple values, we
48// choose the first one. This function should not be used for validation, only
49// for logging or determining which certificate to select for validation.
50std::string GetPreferredIssuerFieldValue(const net::X509Certificate* cert) {
51 if (!cert->issuer().common_name.empty())
52 return cert->issuer().common_name;
53 if (!cert->issuer().organization_names.empty() &&
54 !cert->issuer().organization_names[0].empty())
55 return cert->issuer().organization_names[0];
56 if (!cert->issuer().organization_unit_names.empty() &&
57 !cert->issuer().organization_unit_names[0].empty())
58 return cert->issuer().organization_unit_names[0];
59
60 return std::string();
61}
62
63// The certificate is valid if both are true:
yuweihe4807bad2016-09-30 00:56:5464// * The certificate issuer matches exactly |issuer| or the |issuer| is a
Joe Downing78bfcca2018-11-07 23:43:0565// wildcard.
yuweihe4807bad2016-09-30 00:56:5466// * |now| is within [valid_start, valid_expiry].
67bool IsCertificateValid(const std::string& issuer,
68 const base::Time& now,
mattm436ccfe2017-06-19 20:24:0869 const net::X509Certificate* cert) {
yuweihe4807bad2016-09-30 00:56:5470 return (issuer == kCertIssuerWildCard ||
Joe Downing78bfcca2018-11-07 23:43:0571 issuer == GetPreferredIssuerFieldValue(cert)) &&
72 cert->valid_start() <= now && cert->valid_expiry() > now;
yuweihe4807bad2016-09-30 00:56:5473}
74
75// Returns true if the certificate |c1| is worse than |c2|.
76//
77// Criteria:
78// 1. An invalid certificate is always worse than a valid certificate.
79// 2. Invalid certificates are equally bad, in which case false will be
80// returned.
81// 3. A certificate with earlier |valid_start| time is worse.
82// 4. When |valid_start| are the same, the certificate with earlier
83// |valid_expiry| is worse.
84bool WorseThan(const std::string& issuer,
85 const base::Time& now,
Jeremy Roman3775f2c62017-07-17 20:59:5886 const net::X509Certificate* c1,
87 const net::X509Certificate* c2) {
yuweihe4807bad2016-09-30 00:56:5488 if (!IsCertificateValid(issuer, now, c2))
89 return false;
90
91 if (!IsCertificateValid(issuer, now, c1))
92 return true;
93
94 if (c1->valid_start() != c2->valid_start())
95 return c1->valid_start() < c2->valid_start();
96
97 return c1->valid_expiry() < c2->valid_expiry();
98}
99
David Benjaminac83aab2019-05-29 22:14:34100#if defined(OS_WIN)
101HCERTSTORE OpenLocalMachineCertStore() {
102 return ::CertOpenStore(
103 CERT_STORE_PROV_SYSTEM, 0, NULL,
104 CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG, L"MY");
105}
106#endif
107
[email protected]d95ee262014-02-26 06:30:31108} // namespace
109
110namespace remoting {
111
112TokenValidatorBase::TokenValidatorBase(
113 const ThirdPartyAuthConfig& third_party_auth_config,
114 const std::string& token_scope,
115 scoped_refptr<net::URLRequestContextGetter> request_context_getter)
116 : third_party_auth_config_(third_party_auth_config),
117 token_scope_(token_scope),
118 request_context_getter_(request_context_getter),
Jeremy Roman7c5cfabd2019-08-12 15:45:27119 buffer_(base::MakeRefCounted<net::IOBuffer>(kBufferSize)) {
[email protected]d95ee262014-02-26 06:30:31120 DCHECK(third_party_auth_config_.token_url.is_valid());
121 DCHECK(third_party_auth_config_.token_validation_url.is_valid());
122}
123
Chris Watkins6fe52aa2017-11-28 03:24:05124TokenValidatorBase::~TokenValidatorBase() = default;
[email protected]d95ee262014-02-26 06:30:31125
126// TokenValidator interface.
127void TokenValidatorBase::ValidateThirdPartyToken(
128 const std::string& token,
129 const base::Callback<void(
130 const std::string& shared_secret)>& on_token_validated) {
131 DCHECK(!request_);
132 DCHECK(!on_token_validated.is_null());
133
134 on_token_validated_ = on_token_validated;
lambroslambrouf43816ad2015-12-16 03:50:14135 token_ = token;
[email protected]d95ee262014-02-26 06:30:31136 StartValidateRequest(token);
137}
138
139const GURL& TokenValidatorBase::token_url() const {
140 return third_party_auth_config_.token_url;
141}
142
143const std::string& TokenValidatorBase::token_scope() const {
144 return token_scope_;
145}
146
147// URLFetcherDelegate interface.
maksim.sisovc023fa22016-09-22 04:16:32148void TokenValidatorBase::OnResponseStarted(net::URLRequest* source,
149 int net_result) {
150 DCHECK_NE(net_result, net::ERR_IO_PENDING);
[email protected]d95ee262014-02-26 06:30:31151 DCHECK_EQ(request_.get(), source);
152
mmenke94f1bd92016-12-07 21:13:05153 if (net_result != net::OK) {
154 // Process all network errors in the same manner as read errors.
155 OnReadCompleted(request_.get(), net_result);
maksim.sisovc023fa22016-09-22 04:16:32156 return;
mmenke94f1bd92016-12-07 21:13:05157 }
maksim.sisovc023fa22016-09-22 04:16:32158
159 int bytes_read = request_->Read(buffer_.get(), kBufferSize);
mmenke94f1bd92016-12-07 21:13:05160 if (bytes_read != net::ERR_IO_PENDING)
maksim.sisovc023fa22016-09-22 04:16:32161 OnReadCompleted(request_.get(), bytes_read);
[email protected]d95ee262014-02-26 06:30:31162}
163
164void TokenValidatorBase::OnReadCompleted(net::URLRequest* source,
maksim.sisovc023fa22016-09-22 04:16:32165 int net_result) {
166 DCHECK_NE(net_result, net::ERR_IO_PENDING);
[email protected]d95ee262014-02-26 06:30:31167 DCHECK_EQ(request_.get(), source);
168
maksim.sisovc023fa22016-09-22 04:16:32169 while (net_result > 0) {
170 data_.append(buffer_->data(), net_result);
171 net_result = request_->Read(buffer_.get(), kBufferSize);
[email protected]d95ee262014-02-26 06:30:31172 }
maksim.sisovc023fa22016-09-22 04:16:32173
174 if (net_result == net::ERR_IO_PENDING)
175 return;
176
177 retrying_request_ = false;
178 std::string shared_token = ProcessResponse(net_result);
179 request_.reset();
180 on_token_validated_.Run(shared_token);
[email protected]d95ee262014-02-26 06:30:31181}
182
lambroslambrouf43816ad2015-12-16 03:50:14183void TokenValidatorBase::OnReceivedRedirect(
184 net::URLRequest* request,
185 const net::RedirectInfo& redirect_info,
186 bool* defer_redirect) {
187 if (!retrying_request_ && redirect_info.new_method == "GET" &&
188 redirect_info.new_url == third_party_auth_config_.token_validation_url) {
189 // A sequence of redirects caused the original POST request to become a GET
190 // request for this URL. Cancel the request, and re-submit the POST request.
191 // The chain of redirects are expected to set some cookies that will
192 // ensure the new POST request succeeds.
193 retrying_request_ = true;
194 DCHECK(data_.empty());
195 StartValidateRequest(token_);
196 }
197}
198
[email protected]d95ee262014-02-26 06:30:31199void TokenValidatorBase::OnCertificateRequested(
200 net::URLRequest* source,
201 net::SSLCertRequestInfo* cert_request_info) {
202 DCHECK_EQ(request_.get(), source);
203
204 net::ClientCertStore* client_cert_store;
davidben71f35ff2015-04-17 20:54:48205#if defined(USE_NSS_CERTS)
[email protected]d95ee262014-02-26 06:30:31206 client_cert_store = new net::ClientCertStoreNSS(
207 net::ClientCertStoreNSS::PasswordDelegateFactory());
208#elif defined(OS_WIN)
weitaosub1d542d2015-11-07 02:11:58209 // The network process is running as "Local Service" whose "Current User"
210 // cert store doesn't contain any certificates. Use the "Local Machine"
211 // store instead.
212 // The ACL on the private key of the machine certificate in the "Local
213 // Machine" cert store needs to allow access by "Local Service".
David Benjaminac83aab2019-05-29 22:14:34214 client_cert_store = new net::ClientCertStoreWin(
215 base::BindRepeating(&OpenLocalMachineCertStore));
[email protected]d95ee262014-02-26 06:30:31216#elif defined(OS_MACOSX)
217 client_cert_store = new net::ClientCertStoreMac();
[email protected]d95ee262014-02-26 06:30:31218#else
svaldez2135be52016-04-20 16:34:53219 // OpenSSL does not use the ClientCertStore infrastructure.
220 client_cert_store = nullptr;
[email protected]d95ee262014-02-26 06:30:31221#endif
mattm7ed243f2017-04-28 05:28:58222 // The callback is uncancellable, and GetClientCert requires
223 // client_cert_store to stay alive until the callback is called. So we must
224 // give it a WeakPtr for |this|, and ownership of the other parameters.
[email protected]d95ee262014-02-26 06:30:31225 client_cert_store->GetClientCerts(
mattm7ed243f2017-04-28 05:28:58226 *cert_request_info,
David Benjamin0cda2042019-04-08 23:00:58227 base::BindOnce(&TokenValidatorBase::OnCertificatesSelected,
228 weak_factory_.GetWeakPtr(),
229 base::Owned(client_cert_store)));
[email protected]d95ee262014-02-26 06:30:31230}
231
232void TokenValidatorBase::OnCertificatesSelected(
mattm7ed243f2017-04-28 05:28:58233 net::ClientCertStore* unused,
mattm436ccfe2017-06-19 20:24:08234 net::ClientCertIdentityList selected_certs) {
[email protected]d95ee262014-02-26 06:30:31235 const std::string& issuer =
236 third_party_auth_config_.token_validation_cert_issuer;
yuweihe4807bad2016-09-30 00:56:54237
238 base::Time now = base::Time::Now();
239
Jeremy Roman3775f2c62017-07-17 20:59:58240 auto best_match_position = std::max_element(
241 selected_certs.begin(), selected_certs.end(),
242 [&issuer, now](const std::unique_ptr<net::ClientCertIdentity>& i1,
243 const std::unique_ptr<net::ClientCertIdentity>& i2) {
244 return WorseThan(issuer, now, i1->certificate(), i2->certificate());
245 });
yuweihe4807bad2016-09-30 00:56:54246
mattm7ed243f2017-04-28 05:28:58247 if (best_match_position == selected_certs.end() ||
mattm436ccfe2017-06-19 20:24:08248 !IsCertificateValid(issuer, now, (*best_match_position)->certificate())) {
yuweihe4807bad2016-09-30 00:56:54249 ContinueWithCertificate(nullptr, nullptr);
250 } else {
mattm436ccfe2017-06-19 20:24:08251 scoped_refptr<net::X509Certificate> cert =
252 (*best_match_position)->certificate();
253 net::ClientCertIdentity::SelfOwningAcquirePrivateKey(
254 std::move(*best_match_position),
David Benjamin0cda2042019-04-08 23:00:58255 base::BindOnce(&TokenValidatorBase::ContinueWithCertificate,
256 weak_factory_.GetWeakPtr(), std::move(cert)));
yuweihe4807bad2016-09-30 00:56:54257 }
258}
259
260void TokenValidatorBase::ContinueWithCertificate(
mattm436ccfe2017-06-19 20:24:08261 scoped_refptr<net::X509Certificate> client_cert,
262 scoped_refptr<net::SSLPrivateKey> client_private_key) {
[email protected]d95ee262014-02-26 06:30:31263 if (request_) {
joedowd455f5372017-01-26 00:26:40264 if (client_cert) {
Joe Downing78bfcca2018-11-07 23:43:05265 HOST_LOG << "Using client certificate issued by: '"
266 << GetPreferredIssuerFieldValue(client_cert.get())
267 << "' with start date: '" << client_cert->valid_start()
268 << "' and expiry date: '" << client_cert->valid_expiry() << "'";
joedowd455f5372017-01-26 00:26:40269 }
270
mattm436ccfe2017-06-19 20:24:08271 request_->ContinueWithCertificate(std::move(client_cert),
272 std::move(client_private_key));
[email protected]d95ee262014-02-26 06:30:31273 }
274}
275
276bool TokenValidatorBase::IsValidScope(const std::string& token_scope) {
277 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc.
278 return token_scope == token_scope_;
279}
280
maksim.sisovc023fa22016-09-22 04:16:32281std::string TokenValidatorBase::ProcessResponse(int net_result) {
[email protected]d95ee262014-02-26 06:30:31282 // Verify that we got a successful response.
maksim.sisovc023fa22016-09-22 04:16:32283 if (net_result != net::OK) {
284 LOG(ERROR) << "Error validating token, err=" << net_result;
[email protected]d95ee262014-02-26 06:30:31285 return std::string();
286 }
287
288 int response = request_->GetResponseCode();
289 if (response != 200) {
maksim.sisovc023fa22016-09-22 04:16:32290 LOG(ERROR) << "Error " << response << " validating token: '" << data_
291 << "'";
[email protected]d95ee262014-02-26 06:30:31292 return std::string();
293 }
294
295 // Decode the JSON data from the response.
Lei Zhang48a84332019-02-16 02:35:31296 std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(data_);
[email protected]d95ee262014-02-26 06:30:31297 base::DictionaryValue* dict;
sergeyu2a138b42014-10-01 02:00:40298 if (!value || !value->GetAsDictionary(&dict)) {
[email protected]d95ee262014-02-26 06:30:31299 LOG(ERROR) << "Invalid token validation response: '" << data_ << "'";
300 return std::string();
301 }
302
303 std::string token_scope;
304 dict->GetStringWithoutPathExpansion("scope", &token_scope);
305 if (!IsValidScope(token_scope)) {
maksim.sisovc023fa22016-09-22 04:16:32306 LOG(ERROR) << "Invalid scope: '" << token_scope << "', expected: '"
307 << token_scope_ << "'.";
[email protected]d95ee262014-02-26 06:30:31308 return std::string();
309 }
310
311 std::string shared_secret;
312 // Everything is valid, so return the shared secret to the caller.
313 dict->GetStringWithoutPathExpansion("access_token", &shared_secret);
314 return shared_secret;
315}
316
317} // namespace remoting