Commit 8b30dd49 authored by Daniel McArdle's avatar Daniel McArdle Committed by Commit Bot

Refactor client cert flow with ClientCertificateResponder mojo interface

The problem this CL addresses is that several semantically-different
messages are currently jammed into the response from
OnCertificateRequested. For example, using nullptr for certificate and
private key to signify that no certificate was selected.

This CL eliminates the response from OnCertificateRequested and moves
it into several different messages within the new
ClientCertificateResponder interface.

Bug: 946742
Change-Id: I49c63bdc0732a438336a02e558d4cca4d1202ff6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1589995Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarAntoine Labour <piman@chromium.org>
Reviewed-by: default avatarDavid Benjamin <davidben@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664537}
parent 266de7d3
...@@ -4,9 +4,12 @@ ...@@ -4,9 +4,12 @@
#include "content/browser/network_service_client.h" #include "content/browser/network_service_client.h"
#include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/threading/sequence_bound.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "content/browser/browsing_data/clear_site_data_handler.h" #include "content/browser/browsing_data/clear_site_data_handler.h"
#include "content/browser/devtools/devtools_url_loader_interceptor.h" #include "content/browser/devtools/devtools_url_loader_interceptor.h"
...@@ -65,107 +68,67 @@ class SSLErrorDelegate : public SSLErrorHandler::Delegate { ...@@ -65,107 +68,67 @@ class SSLErrorDelegate : public SSLErrorHandler::Delegate {
base::WeakPtrFactory<SSLErrorDelegate> weak_factory_; base::WeakPtrFactory<SSLErrorDelegate> weak_factory_;
}; };
// This class is created on UI thread, and deleted by // This class lives on the IO thread. It is self-owned and will delete itself
// BrowserThread::DeleteSoon() after the |callback_| runs. The |callback_| // after any of the SSLClientAuthHandler::Delegate methods are invoked (or when
// needs to run on UI thread since it is called through the // a mojo connection error occurs).
// NetworkServiceClient interface.
//
// The |ssl_client_auth_handler_| needs to be created on IO thread, and deleted
// on the same thread by posting a BrowserThread::DeleteSoon() task to IO
// thread.
//
// ContinueWithCertificate() and CancelCertificateSelection() run on IO thread.
class SSLClientAuthDelegate : public SSLClientAuthHandler::Delegate { class SSLClientAuthDelegate : public SSLClientAuthHandler::Delegate {
public: public:
SSLClientAuthDelegate( SSLClientAuthDelegate(
network::mojom::NetworkServiceClient::OnCertificateRequestedCallback network::mojom::ClientCertificateResponderPtrInfo
callback, client_cert_responder_info,
content::ResourceContext* resource_context,
ResourceRequestInfo::WebContentsGetter web_contents_getter, ResourceRequestInfo::WebContentsGetter web_contents_getter,
scoped_refptr<net::SSLCertRequestInfo> cert_info) const scoped_refptr<net::SSLCertRequestInfo>& cert_info)
: callback_(std::move(callback)), cert_info_(std::move(cert_info)) { : client_cert_responder_(std::move(client_cert_responder_info)),
content::WebContents* web_contents = web_contents_getter.Run(); ssl_client_auth_handler_(std::make_unique<SSLClientAuthHandler>(
content::BrowserContext* browser_context = GetContentClient()->browser()->CreateClientCertStore(
web_contents->GetBrowserContext(); resource_context),
content::ResourceContext* resource_context = std::move(web_contents_getter),
browser_context->GetResourceContext(); std::move(cert_info.get()),
base::PostTaskWithTraits( this)) {
FROM_HERE, {BrowserThread::IO}, DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::BindOnce(&SSLClientAuthDelegate::CreateSSLClientAuthHandler, DCHECK(client_cert_responder_);
base::Unretained(this), resource_context, ssl_client_auth_handler_->SelectCertificate();
web_contents_getter)); client_cert_responder_.set_connection_error_handler(base::BindOnce(
&SSLClientAuthDelegate::DeleteSelf, base::Unretained(this)));
}
~SSLClientAuthDelegate() override { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
void DeleteSelf() { delete this; }
// SSLClientAuthHandler::Delegate:
void CancelCertificateSelection() override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
client_cert_responder_->CancelRequest();
DeleteSelf();
} }
~SSLClientAuthDelegate() override {}
// SSLClientAuthHandler::Delegate: // SSLClientAuthHandler::Delegate:
void ContinueWithCertificate( void ContinueWithCertificate(
scoped_refptr<net::X509Certificate> cert, scoped_refptr<net::X509Certificate> cert,
scoped_refptr<net::SSLPrivateKey> private_key) override { scoped_refptr<net::SSLPrivateKey> private_key) override {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK((cert && private_key) || (!cert && !private_key)); DCHECK((cert && private_key) || (!cert && !private_key));
std::string provider_name; if (cert && private_key) {
std::vector<uint16_t> algorithm_preferences;
network::mojom::SSLPrivateKeyPtr ssl_private_key; network::mojom::SSLPrivateKeyPtr ssl_private_key;
auto ssl_private_key_request = mojo::MakeRequest(&ssl_private_key);
if (private_key) {
provider_name = private_key->GetProviderName();
algorithm_preferences = private_key->GetAlgorithmPreferences();
mojo::MakeStrongBinding(
std::make_unique<SSLPrivateKeyImpl>(std::move(private_key)),
std::move(ssl_private_key_request));
}
base::PostTaskWithTraits( mojo::MakeStrongBinding(std::make_unique<SSLPrivateKeyImpl>(private_key),
FROM_HERE, {BrowserThread::UI}, mojo::MakeRequest(&ssl_private_key));
base::BindOnce(&SSLClientAuthDelegate::RunCallback,
base::Unretained(this), cert, std::move(provider_name),
std::move(algorithm_preferences),
std::move(ssl_private_key),
false /* cancel_certificate_selection */));
}
// SSLClientAuthHandler::Delegate:
void CancelCertificateSelection() override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
network::mojom::SSLPrivateKeyPtr ssl_private_key; client_cert_responder_->ContinueWithCertificate(
mojo::MakeRequest(&ssl_private_key); cert, private_key->GetProviderName(),
base::PostTaskWithTraits( private_key->GetAlgorithmPreferences(), std::move(ssl_private_key));
FROM_HERE, {BrowserThread::UI}, } else {
base::BindOnce(&SSLClientAuthDelegate::RunCallback, client_cert_responder_->ContinueWithoutCertificate();
base::Unretained(this), nullptr, std::string(),
std::vector<uint16_t>(), std::move(ssl_private_key),
true /* cancel_certificate_selection */));
} }
void RunCallback(scoped_refptr<net::X509Certificate> cert, DeleteSelf();
std::string provider_name,
std::vector<uint16_t> algorithm_preferences,
network::mojom::SSLPrivateKeyPtr ssl_private_key,
bool cancel_certificate_selection) {
std::move(callback_).Run(cert, provider_name, algorithm_preferences,
std::move(ssl_private_key),
cancel_certificate_selection);
BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, this);
} }
private: private:
void CreateSSLClientAuthHandler( network::mojom::ClientCertificateResponderPtr client_cert_responder_;
content::ResourceContext* resource_context,
ResourceRequestInfo::WebContentsGetter web_contents_getter) {
std::unique_ptr<net::ClientCertStore> client_cert_store =
GetContentClient()->browser()->CreateClientCertStore(resource_context);
ssl_client_auth_handler_.reset(new SSLClientAuthHandler(
std::move(client_cert_store), std::move(web_contents_getter),
cert_info_.get(), this));
ssl_client_auth_handler_->SelectCertificate();
}
network::mojom::NetworkServiceClient::OnCertificateRequestedCallback
callback_;
scoped_refptr<net::SSLCertRequestInfo> cert_info_;
std::unique_ptr<SSLClientAuthHandler> ssl_client_auth_handler_; std::unique_ptr<SSLClientAuthHandler> ssl_client_auth_handler_;
}; };
...@@ -344,28 +307,45 @@ bool IsMainFrameRequest(int process_id, int routing_id) { ...@@ -344,28 +307,45 @@ bool IsMainFrameRequest(int process_id, int routing_id) {
return frame_tree_node && frame_tree_node->IsMainFrame(); return frame_tree_node && frame_tree_node->IsMainFrame();
} }
void CreateSSLClientAuthDelegateOnIO(
network::mojom::ClientCertificateResponderPtrInfo
client_cert_responder_info,
content::ResourceContext* resource_context,
ResourceRequestInfo::WebContentsGetter web_contents_getter,
scoped_refptr<net::SSLCertRequestInfo> cert_info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
new SSLClientAuthDelegate(std::move(client_cert_responder_info),
resource_context, std::move(web_contents_getter),
cert_info); // deletes self
}
void OnCertificateRequestedContinuation( void OnCertificateRequestedContinuation(
uint32_t process_id, uint32_t process_id,
uint32_t routing_id, uint32_t routing_id,
uint32_t request_id, uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info, const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
network::mojom::NetworkServiceClient::OnCertificateRequestedCallback network::mojom::ClientCertificateResponderPtrInfo
callback, client_cert_responder_info,
base::RepeatingCallback<WebContents*(void)> web_contents_getter) { base::RepeatingCallback<WebContents*(void)> web_contents_getter) {
if (!web_contents_getter) { if (!web_contents_getter) {
web_contents_getter = web_contents_getter =
base::BindRepeating(GetWebContents, process_id, routing_id); base::BindRepeating(GetWebContents, process_id, routing_id);
} }
if (!web_contents_getter.Run()) { WebContents* web_contents = web_contents_getter.Run();
network::mojom::SSLPrivateKeyPtr ssl_private_key; if (!web_contents) {
mojo::MakeRequest(&ssl_private_key); DCHECK(client_cert_responder_info);
std::move(callback).Run(nullptr, std::string(), std::vector<uint16_t>(), network::mojom::ClientCertificateResponderPtr client_cert_responder(
std::move(ssl_private_key), std::move(client_cert_responder_info));
true /* cancel_certificate_selection */); client_cert_responder->CancelRequest();
return; return;
} }
new SSLClientAuthDelegate(std::move(callback), std::move(web_contents_getter),
cert_info); // deletes self base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&CreateSSLClientAuthDelegateOnIO,
std::move(client_cert_responder_info),
web_contents->GetBrowserContext()->GetResourceContext(),
std::move(web_contents_getter), cert_info));
} }
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
...@@ -452,20 +432,21 @@ void NetworkServiceClient::OnCertificateRequested( ...@@ -452,20 +432,21 @@ void NetworkServiceClient::OnCertificateRequested(
uint32_t routing_id, uint32_t routing_id,
uint32_t request_id, uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info, const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
network::mojom::NetworkServiceClient::OnCertificateRequestedCallback network::mojom::ClientCertificateResponderPtr cert_responder) {
callback) {
// Use |window_id| if it's provided. // Use |window_id| if it's provided.
if (window_id) { if (window_id) {
base::PostTaskWithTraitsAndReplyWithResult( base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {BrowserThread::IO}, FROM_HERE, {BrowserThread::IO},
base::BindOnce(&GetWebContentsFromRegistry, *window_id), base::BindOnce(&GetWebContentsFromRegistry, *window_id),
base::BindOnce(&OnCertificateRequestedContinuation, process_id, base::BindOnce(&OnCertificateRequestedContinuation, process_id,
routing_id, request_id, cert_info, std::move(callback))); routing_id, request_id, cert_info,
cert_responder.PassInterface()));
return; return;
} }
OnCertificateRequestedContinuation(process_id, routing_id, request_id, OnCertificateRequestedContinuation(process_id, routing_id, request_id,
cert_info, std::move(callback), {}); cert_info, cert_responder.PassInterface(),
{});
} }
void NetworkServiceClient::OnSSLCertificateError( void NetworkServiceClient::OnSSLCertificateError(
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#ifndef CONTENT_BROWSER_NETWORK_SERVICE_IMPL_H_ #ifndef CONTENT_BROWSER_NETWORK_SERVICE_IMPL_H_
#define CONTENT_BROWSER_NETWORK_SERVICE_IMPL_H_ #define CONTENT_BROWSER_NETWORK_SERVICE_IMPL_H_
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/memory_pressure_listener.h" #include "base/memory/memory_pressure_listener.h"
#include "build/build_config.h" #include "build/build_config.h"
...@@ -50,8 +54,7 @@ class CONTENT_EXPORT NetworkServiceClient ...@@ -50,8 +54,7 @@ class CONTENT_EXPORT NetworkServiceClient
uint32_t routing_id, uint32_t routing_id,
uint32_t request_id, uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info, const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
network::mojom::NetworkServiceClient::OnCertificateRequestedCallback network::mojom::ClientCertificateResponderPtr cert_responder) override;
callback) override;
void OnSSLCertificateError(uint32_t process_id, void OnSSLCertificateError(uint32_t process_id,
uint32_t routing_id, uint32_t routing_id,
const GURL& url, const GURL& url,
......
...@@ -49,6 +49,37 @@ interface AuthChallengeResponder { ...@@ -49,6 +49,37 @@ interface AuthChallengeResponder {
OnAuthCredentials(AuthCredentials? credentials); OnAuthCredentials(AuthCredentials? credentials);
}; };
// This interface enables the UI to send client certificate selections back to
// the network service.
//
// Defining an interface for this purpose, rather than using a union in the
// response of OnCertificateRequested, enables the NetworkServiceClient to learn
// of the URLLoader destruction via the connection error handler.
interface ClientCertificateResponder {
// Use the selected certificate and continue the URLRequest.
//
// - |provider_name| corresponds to the return value of
// net::SSLPrivateKey::GetProviderName().
// - |algorithm_preferences| corresponds to the return value of
// net::SSLPrivateKey::GetAlgorithmPreferences().
ContinueWithCertificate(network.mojom.X509Certificate x509_certificate,
string provider_name,
array<uint16> algorithm_preferences,
SSLPrivateKey ssl_private_key);
// Affirmatively select no certificate (this is cached and can affect future
// URLRequests). Does not cancel the URLRequest.
//
// The connection is continued with no client cert.
// net::URLRequest::ContinueWithCertificate(nullptr, nullptr) needs to be
// called.
ContinueWithoutCertificate();
// Cancel the URLRequest. The request is aborted.
// net::URLRequest::CancelWithError() needs to be called.
CancelRequest();
};
struct LoadInfo { struct LoadInfo {
uint32 process_id; uint32 process_id;
uint32 routing_id; uint32 routing_id;
...@@ -73,34 +104,22 @@ interface NetworkServiceClient { ...@@ -73,34 +104,22 @@ interface NetworkServiceClient {
AuthChallengeInfo auth_info, AuthChallengeInfo auth_info,
URLResponseHead? head, URLResponseHead? head,
AuthChallengeResponder auth_challenge_responder); AuthChallengeResponder auth_challenge_responder);
// Called when an SSL certificate requested message is received for client // Called when an SSL certificate requested message is received for client
// authentication. // authentication.
// The |provider_name| parameter corresponses to the return value of
// net::SSLPrivateKey::GetProviderName().
// The |algorithm_preferences| parameter corresponds to the return value
// of net::SSLPrivateKey::GetAlgorithmPreferences().
// The |cancel_certificate_selection| parameter is used to distinguish
// between the following two cases because the |x509_certificate| will be
// nullptr in both cases:
// 1. The connection is continued with no client cert,
// net::URLRequest::ContinueWithCertificate(nullptr, nullptr) needs to be
// called.
// 2. The request is aborted, net::URLRequest::CancelWithError() needs to be
// called.
// //
// |window_id| or else |process_id| and |routing_id| indicates // Rather than using one response for multiple purposes, the caller expects
// the frame making the request, see // exactly one response (or disconnect) to be sent back via |cert_responder|.
// network::ResourceRequest::fetch_window_id. //
// |window_id| or else |process_id| and |routing_id| indicates the frame
// making the request, see network::ResourceRequest::fetch_window_id.
OnCertificateRequested(mojo_base.mojom.UnguessableToken? window_id, OnCertificateRequested(mojo_base.mojom.UnguessableToken? window_id,
uint32 process_id, uint32 process_id,
uint32 routing_id, uint32 routing_id,
uint32 request_id, uint32 request_id,
network.mojom.SSLCertRequestInfo cert_info) => ( network.mojom.SSLCertRequestInfo cert_info,
network.mojom.X509Certificate x509_certificate, ClientCertificateResponder cert_responder);
string provider_name,
array<uint16> algorithm_preferences,
SSLPrivateKey ssl_private_key,
bool cancel_certificate_selection);
// Called when an SSL certificate is encountered. // Called when an SSL certificate is encountered.
// The callback argument is a net::ERROR value. If it's net::OK, then the // The callback argument is a net::ERROR value. If it's net::OK, then the
// request is resumed. Otherwise it's cancelled with the given error. // request is resumed. Otherwise it's cancelled with the given error.
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "services/network/test/test_network_service_client.h" #include "services/network/test/test_network_service_client.h"
#include <utility>
#include "base/optional.h" #include "base/optional.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
...@@ -36,7 +38,7 @@ void TestNetworkServiceClient::OnCertificateRequested( ...@@ -36,7 +38,7 @@ void TestNetworkServiceClient::OnCertificateRequested(
uint32_t routing_id, uint32_t routing_id,
uint32_t request_id, uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info, const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
mojom::NetworkServiceClient::OnCertificateRequestedCallback callback) { mojom::ClientCertificateResponderPtr client_cert_responder) {
NOTREACHED(); NOTREACHED();
} }
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#ifndef SERVICES_NETWORK_TEST_TEST_NETWORK_SERVICE_CLIENT_H_ #ifndef SERVICES_NETWORK_TEST_TEST_NETWORK_SERVICE_CLIENT_H_
#define SERVICES_NETWORK_TEST_TEST_NETWORK_SERVICE_CLIENT_H_ #define SERVICES_NETWORK_TEST_TEST_NETWORK_SERVICE_CLIENT_H_
#include <string>
#include <vector>
#include "build/build_config.h" #include "build/build_config.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
#include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/network_service.mojom.h"
...@@ -43,8 +46,7 @@ class TestNetworkServiceClient : public network::mojom::NetworkServiceClient { ...@@ -43,8 +46,7 @@ class TestNetworkServiceClient : public network::mojom::NetworkServiceClient {
uint32_t routing_id, uint32_t routing_id,
uint32_t request_id, uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info, const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
mojom::NetworkServiceClient::OnCertificateRequestedCallback callback) mojom::ClientCertificateResponderPtr client_cert_responder) override;
override;
void OnSSLCertificateError(uint32_t process_id, void OnSSLCertificateError(uint32_t process_id,
uint32_t routing_id, uint32_t routing_id,
const GURL& url, const GURL& url,
......
...@@ -332,6 +332,7 @@ URLLoader::URLLoader( ...@@ -332,6 +332,7 @@ URLLoader::URLLoader(
do_not_prompt_for_login_(request.do_not_prompt_for_login), do_not_prompt_for_login_(request.do_not_prompt_for_login),
binding_(this, std::move(url_loader_request)), binding_(this, std::move(url_loader_request)),
auth_challenge_responder_binding_(this), auth_challenge_responder_binding_(this),
client_cert_responder_binding_(this),
url_loader_client_(std::move(url_loader_client)), url_loader_client_(std::move(url_loader_client)),
writable_handle_watcher_(FROM_HERE, writable_handle_watcher_(FROM_HERE,
mojo::SimpleWatcher::ArmingPolicy::MANUAL, mojo::SimpleWatcher::ArmingPolicy::MANUAL,
...@@ -776,33 +777,40 @@ void URLLoader::OnAuthRequired(net::URLRequest* url_request, ...@@ -776,33 +777,40 @@ void URLLoader::OnAuthRequired(net::URLRequest* url_request,
void URLLoader::OnCertificateRequested(net::URLRequest* unused, void URLLoader::OnCertificateRequested(net::URLRequest* unused,
net::SSLCertRequestInfo* cert_info) { net::SSLCertRequestInfo* cert_info) {
DCHECK(!client_cert_responder_binding_.is_bound());
if (base::CommandLine::ForCurrentProcess()->HasSwitch( if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kIgnoreUrlFetcherCertRequests) && switches::kIgnoreUrlFetcherCertRequests) &&
factory_params_->process_id == 0 && factory_params_->process_id == 0 &&
render_frame_id_ == MSG_ROUTING_NONE) { render_frame_id_ == MSG_ROUTING_NONE) {
url_request_->ContinueWithCertificate(nullptr, nullptr); ContinueWithoutCertificate();
return; return;
} }
if (!network_service_client_) { if (!network_service_client_) {
OnCertificateRequestedResponse(nullptr, std::string(), ContinueWithoutCertificate();
std::vector<uint16_t>(), nullptr,
true /* cancel_certificate_selection */);
return; return;
} }
// Set up mojo endpoints for ClientCertificateResponder and bind to the
// InterfaceRequest. This enables us to receive messages regarding the client
// certificate selection.
mojom::ClientCertificateResponderPtr client_cert_responder;
auto client_cert_responder_request =
mojo::MakeRequest(&client_cert_responder);
client_cert_responder_binding_.Bind(std::move(client_cert_responder_request));
client_cert_responder_binding_.set_connection_error_handler(
base::BindOnce(&URLLoader::CancelRequest, base::Unretained(this)));
if (fetch_window_id_) { if (fetch_window_id_) {
network_service_client_->OnCertificateRequested( network_service_client_->OnCertificateRequested(
fetch_window_id_, -1 /* process_id */, -1 /* routing_id */, request_id_, fetch_window_id_, -1 /* process_id */, -1 /* routing_id */, request_id_,
cert_info, cert_info, std::move(client_cert_responder));
base::BindOnce(&URLLoader::OnCertificateRequestedResponse,
weak_ptr_factory_.GetWeakPtr()));
} else { } else {
network_service_client_->OnCertificateRequested( network_service_client_->OnCertificateRequested(
base::nullopt /* window_id */, factory_params_->process_id, base::nullopt /* window_id */, factory_params_->process_id,
render_frame_id_, request_id_, cert_info, render_frame_id_, request_id_, cert_info,
base::BindOnce(&URLLoader::OnCertificateRequestedResponse, std::move(client_cert_responder));
weak_ptr_factory_.GetWeakPtr()));
} }
} }
...@@ -1177,6 +1185,28 @@ void URLLoader::OnAuthCredentials( ...@@ -1177,6 +1185,28 @@ void URLLoader::OnAuthCredentials(
} }
} }
void URLLoader::ContinueWithCertificate(
const scoped_refptr<net::X509Certificate>& x509_certificate,
const std::string& provider_name,
const std::vector<uint16_t>& algorithm_preferences,
mojom::SSLPrivateKeyPtr ssl_private_key) {
client_cert_responder_binding_.Close();
auto key = base::MakeRefCounted<SSLPrivateKeyInternal>(
provider_name, algorithm_preferences, std::move(ssl_private_key));
url_request_->ContinueWithCertificate(std::move(x509_certificate),
std::move(key));
}
void URLLoader::ContinueWithoutCertificate() {
client_cert_responder_binding_.Close();
url_request_->ContinueWithCertificate(nullptr, nullptr);
}
void URLLoader::CancelRequest() {
client_cert_responder_binding_.Close();
url_request_->CancelWithError(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
}
void URLLoader::NotifyCompleted(int error_code) { void URLLoader::NotifyCompleted(int error_code) {
// Ensure sending the final upload progress message here, since // Ensure sending the final upload progress message here, since
// OnResponseCompleted can be called without OnResponseStarted on cancellation // OnResponseCompleted can be called without OnResponseStarted on cancellation
...@@ -1315,26 +1345,6 @@ void URLLoader::OnSSLCertificateErrorResponse(const net::SSLInfo& ssl_info, ...@@ -1315,26 +1345,6 @@ void URLLoader::OnSSLCertificateErrorResponse(const net::SSLInfo& ssl_info,
url_request_->CancelWithSSLError(net_error, ssl_info); url_request_->CancelWithSSLError(net_error, ssl_info);
} }
void URLLoader::OnCertificateRequestedResponse(
const scoped_refptr<net::X509Certificate>& x509_certificate,
const std::string& provider_name,
const std::vector<uint16_t>& algorithm_preferences,
mojom::SSLPrivateKeyPtr ssl_private_key,
bool cancel_certificate_selection) {
if (cancel_certificate_selection) {
url_request_->CancelWithError(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
} else {
if (x509_certificate) {
auto key = base::MakeRefCounted<SSLPrivateKeyInternal>(
provider_name, algorithm_preferences, std::move(ssl_private_key));
url_request_->ContinueWithCertificate(std::move(x509_certificate),
std::move(key));
} else {
url_request_->ContinueWithCertificate(nullptr, nullptr);
}
}
}
bool URLLoader::HasDataPipe() const { bool URLLoader::HasDataPipe() const {
return pending_write_ || response_body_stream_.is_valid(); return pending_write_ || response_body_stream_.is_valid();
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <string>
#include <vector> #include <vector>
#include "base/callback.h" #include "base/callback.h"
...@@ -51,7 +52,8 @@ class ScopedThrottlingToken; ...@@ -51,7 +52,8 @@ class ScopedThrottlingToken;
class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
: public mojom::URLLoader, : public mojom::URLLoader,
public net::URLRequest::Delegate, public net::URLRequest::Delegate,
public mojom::AuthChallengeResponder { public mojom::AuthChallengeResponder,
public mojom::ClientCertificateResponder {
public: public:
using DeleteCallback = base::OnceCallback<void(mojom::URLLoader* loader)>; using DeleteCallback = base::OnceCallback<void(mojom::URLLoader* loader)>;
...@@ -113,6 +115,15 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader ...@@ -113,6 +115,15 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
void OnAuthCredentials( void OnAuthCredentials(
const base::Optional<net::AuthCredentials>& credentials) override; const base::Optional<net::AuthCredentials>& credentials) override;
// mojom::ClientCertificateResponder:
void ContinueWithCertificate(
const scoped_refptr<net::X509Certificate>& x509_certificate,
const std::string& provider_name,
const std::vector<uint16_t>& algorithm_preferences,
mojom::SSLPrivateKeyPtr ssl_private_key) override;
void ContinueWithoutCertificate() override;
void CancelRequest() override;
net::LoadState GetLoadStateForTesting() const; net::LoadState GetLoadStateForTesting() const;
uint32_t GetRenderFrameId() const; uint32_t GetRenderFrameId() const;
...@@ -190,12 +201,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader ...@@ -190,12 +201,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
void OnUploadProgressACK(); void OnUploadProgressACK();
void OnSSLCertificateErrorResponse(const net::SSLInfo& ssl_info, void OnSSLCertificateErrorResponse(const net::SSLInfo& ssl_info,
int net_error); int net_error);
void OnCertificateRequestedResponse(
const scoped_refptr<net::X509Certificate>& x509_certificate,
const std::string& provider_name,
const std::vector<uint16_t>& algorithm_preferences,
mojom::SSLPrivateKeyPtr ssl_private_key,
bool cancel_certificate_selection);
bool HasDataPipe() const; bool HasDataPipe() const;
void RecordBodyReadFromNetBeforePausedIfNeeded(); void RecordBodyReadFromNetBeforePausedIfNeeded();
void ResumeStart(); void ResumeStart();
...@@ -247,6 +252,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader ...@@ -247,6 +252,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
mojo::Binding<mojom::URLLoader> binding_; mojo::Binding<mojom::URLLoader> binding_;
mojo::Binding<mojom::AuthChallengeResponder> mojo::Binding<mojom::AuthChallengeResponder>
auth_challenge_responder_binding_; auth_challenge_responder_binding_;
mojo::Binding<mojom::ClientCertificateResponder>
client_cert_responder_binding_;
mojom::URLLoaderClientPtr url_loader_client_; mojom::URLLoaderClientPtr url_loader_client_;
int64_t total_written_bytes_ = 0; int64_t total_written_bytes_ = 0;
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/data_pipe_utils.h" #include "mojo/public/cpp/system/data_pipe_utils.h"
#include "mojo/public/cpp/system/wait.h" #include "mojo/public/cpp/system/wait.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/load_flags.h" #include "net/base/load_flags.h"
#include "net/base/mime_sniffer.h" #include "net/base/mime_sniffer.h"
...@@ -2131,6 +2132,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { ...@@ -2131,6 +2132,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient {
NULL_CERTIFICATE, NULL_CERTIFICATE,
VALID_CERTIFICATE_SIGNATURE, VALID_CERTIFICATE_SIGNATURE,
INVALID_CERTIFICATE_SIGNATURE, INVALID_CERTIFICATE_SIGNATURE,
DESTROY_CLIENT_CERT_RESPONDER,
}; };
// mojom::NetworkServiceClient: // mojom::NetworkServiceClient:
...@@ -2170,8 +2172,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { ...@@ -2170,8 +2172,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient {
uint32_t routing_id, uint32_t routing_id,
uint32_t request_id, uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info, const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
mojom::NetworkServiceClient::OnCertificateRequestedCallback callback) mojom::ClientCertificateResponderPtr client_cert_responder) override {
override {
switch (certificate_response_) { switch (certificate_response_) {
case CertificateResponse::INVALID: case CertificateResponse::INVALID:
NOTREACHED(); NOTREACHED();
...@@ -2181,21 +2182,19 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { ...@@ -2181,21 +2182,19 @@ class MockNetworkServiceClient : public TestNetworkServiceClient {
url_loader_ptr_->reset(); url_loader_ptr_->reset();
break; break;
case CertificateResponse::CANCEL_CERTIFICATE_SELECTION: case CertificateResponse::CANCEL_CERTIFICATE_SELECTION:
std::move(callback).Run(nullptr, std::string(), std::vector<uint16_t>(), client_cert_responder->CancelRequest();
nullptr,
true /* cancel_certificate_selection */);
break; break;
case CertificateResponse::NULL_CERTIFICATE: case CertificateResponse::NULL_CERTIFICATE:
std::move(callback).Run(nullptr, std::string(), std::vector<uint16_t>(), client_cert_responder->ContinueWithoutCertificate();
nullptr,
false /* cancel_certificate_selection */);
break; break;
case CertificateResponse::VALID_CERTIFICATE_SIGNATURE: case CertificateResponse::VALID_CERTIFICATE_SIGNATURE:
case CertificateResponse::INVALID_CERTIFICATE_SIGNATURE: case CertificateResponse::INVALID_CERTIFICATE_SIGNATURE:
std::move(callback).Run(std::move(certificate_), provider_name_, client_cert_responder->ContinueWithCertificate(
algorithm_preferences_, std::move(certificate_), provider_name_, algorithm_preferences_,
std::move(ssl_private_key_ptr_), std::move(ssl_private_key_ptr_));
false /* cancel_certificate_selection */); break;
case CertificateResponse::DESTROY_CLIENT_CERT_RESPONDER:
// Send no response and let the local variable be destroyed.
break; break;
} }
++on_certificate_requested_counter_; ++on_certificate_requested_counter_;
...@@ -2641,6 +2640,119 @@ class TestSSLPrivateKey : public net::SSLPrivateKey { ...@@ -2641,6 +2640,119 @@ class TestSSLPrivateKey : public net::SSLPrivateKey {
}; };
#if !defined(OS_IOS) #if !defined(OS_IOS)
TEST_F(URLLoaderTest, ClientAuthRespondTwice) {
// This tests that one URLLoader can handle two client cert requests.
net::SSLServerConfig ssl_config;
ssl_config.client_cert_type =
net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT;
net::EmbeddedTestServer test_server_1(net::EmbeddedTestServer::TYPE_HTTPS);
test_server_1.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config);
test_server_1.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("services/test/data")));
ASSERT_TRUE(test_server_1.Start());
net::EmbeddedTestServer test_server_2(net::EmbeddedTestServer::TYPE_HTTPS);
test_server_2.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config);
test_server_2.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("services/test/data")));
ASSERT_TRUE(test_server_2.Start());
std::unique_ptr<net::FakeClientCertIdentity> identity =
net::FakeClientCertIdentity::CreateFromCertAndKeyFiles(
net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8");
ASSERT_TRUE(identity);
scoped_refptr<TestSSLPrivateKey> private_key =
base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key());
MockNetworkServiceClient network_service_client;
network_service_client.set_certificate_response(
MockNetworkServiceClient::CertificateResponse::
VALID_CERTIFICATE_SIGNATURE);
network_service_client.set_private_key(private_key);
network_service_client.set_certificate(identity->certificate());
// Create a request to server_1 that will redirect to server_2
ResourceRequest request = CreateResourceRequest(
"GET",
test_server_1.GetURL("/server-redirect-307?" +
net::EscapeQueryParamValue(
test_server_2.GetURL("/echo").spec(), true)));
base::RunLoop delete_run_loop;
mojom::URLLoaderPtr loader;
std::unique_ptr<URLLoader> url_loader;
mojom::URLLoaderFactoryParams params;
params.process_id = kProcessId;
params.is_corb_enabled = false;
url_loader = std::make_unique<URLLoader>(
context(), &network_service_client,
DeleteLoaderCallback(&delete_run_loop, &url_loader),
mojo::MakeRequest(&loader), mojom::kURLLoadOptionNone, request,
client()->CreateInterfacePtr(), TRAFFIC_ANNOTATION_FOR_TESTS, &params,
0 /* request_id */, resource_scheduler_client(), nullptr,
nullptr /* network_usage_accumulator */, nullptr /* header_client */);
EXPECT_EQ(0, network_service_client.on_certificate_requested_counter());
EXPECT_EQ(0, private_key->sign_count());
client()->RunUntilRedirectReceived();
loader->FollowRedirect({}, {}, base::nullopt);
// MockNetworkServiceClient gives away the private key when it invokes
// ContinueWithCertificate, so we have to give it the key again.
network_service_client.set_private_key(private_key);
client()->RunUntilComplete();
delete_run_loop.Run();
EXPECT_EQ(net::OK, client()->completion_status().error_code);
EXPECT_EQ(2, network_service_client.on_certificate_requested_counter());
EXPECT_EQ(2, private_key->sign_count());
}
TEST_F(URLLoaderTest, ClientAuthDestroyResponder) {
// When URLLoader receives no message from the ClientCertificateResponder and
// its connection errors out, we expect the request to be canceled rather than
// just hang.
net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
net::SSLServerConfig ssl_config;
ssl_config.client_cert_type =
net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT;
test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config);
test_server.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("services/test/data")));
ASSERT_TRUE(test_server.Start());
MockNetworkServiceClient network_service_client;
network_service_client.set_certificate_response(
MockNetworkServiceClient::CertificateResponse::
DESTROY_CLIENT_CERT_RESPONDER);
ResourceRequest request =
CreateResourceRequest("GET", test_server.GetURL("/defaultresponse"));
base::RunLoop delete_run_loop;
mojom::URLLoaderPtr loader;
mojom::URLLoaderFactoryParams params;
params.process_id = kProcessId;
params.is_corb_enabled = false;
std::unique_ptr<URLLoader> url_loader = std::make_unique<URLLoader>(
context(), &network_service_client,
DeleteLoaderCallback(&delete_run_loop, &url_loader),
mojo::MakeRequest(&loader), 0, request, client()->CreateInterfacePtr(),
TRAFFIC_ANNOTATION_FOR_TESTS, &params, 0 /* request_id */,
resource_scheduler_client(), nullptr,
nullptr /* network_usage_accumulator */, nullptr /* header_client */);
network_service_client.set_url_loader_ptr(&loader);
RunUntilIdle();
ASSERT_TRUE(url_loader);
client()->RunUntilComplete();
EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
client()->completion_status().error_code);
}
TEST_F(URLLoaderTest, ClientAuthCancelConnection) { TEST_F(URLLoaderTest, ClientAuthCancelConnection) {
net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
net::SSLServerConfig ssl_config; net::SSLServerConfig ssl_config;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment