From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Wed, 8 May 2019 17:25:55 -0700
Subject: network_service_allow_remote_certificate_verification_logic.patch

This adds a callback from the network service that's used to implement
session.setCertificateVerifyCallback.

diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 4547a0dc6956204da4a8b6d8ac203a78b678767a..8dd57dc6d943054bda0e3ab9d992732f36ec6d3e 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -150,6 +150,11 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
+// Electron
+#include "net/cert/caching_cert_verifier.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/multi_threaded_cert_verifier.h"
+
 #if BUILDFLAG(IS_CT_SUPPORTED)
 // gn check does not account for BUILDFLAG(). So, for iOS builds, it will
 // complain about a missing dependency on the target exposing this header. Add a
@@ -513,6 +518,99 @@ bool GetFullDataFilePath(
 
 }  // namespace
 
+class RemoteCertVerifier : public net::CertVerifier {
+ public:
+  class Request : public net::CertVerifier::Request {
+   public:
+    Request() {}
+    ~Request() override = default;
+    void OnRemoteResponse(
+        const RequestParams& params,
+        net::CertVerifyResult* verify_result,
+        int error_from_upstream,
+        net::CompletionOnceCallback callback,
+        int error_from_client,
+        const net::CertVerifyResult& verify_result_from_client) {
+      if (error_from_client == net::ERR_ABORTED) {
+        // use the default
+        std::move(callback).Run(error_from_upstream);
+      } else {
+        // use the override
+        verify_result->Reset();
+        verify_result->verified_cert = verify_result_from_client.verified_cert;
+        std::move(callback).Run(error_from_client);
+      }
+    }
+    base::WeakPtr<Request> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+   private:
+    base::WeakPtrFactory<Request> weak_factory_{this};
+  };
+
+  RemoteCertVerifier(std::unique_ptr<net::CertVerifier> upstream): upstream_(std::move(upstream)) {
+  }
+  ~RemoteCertVerifier() override = default;
+
+  void Bind(
+      mojo::PendingRemote<mojom::CertVerifierClient> client_info) {
+    client_.reset();
+    if (client_info.is_valid()) {
+      client_.Bind(std::move(client_info));
+    }
+  }
+
+  // CertVerifier implementation
+  int Verify(const RequestParams& params,
+             net::CertVerifyResult* verify_result,
+             net::CompletionOnceCallback callback,
+             std::unique_ptr<CertVerifier::Request>* out_req,
+             const net::NetLogWithSource& net_log) override {
+    out_req->reset();
+
+    net::CompletionOnceCallback callback2 = base::BindOnce(
+        &RemoteCertVerifier::OnRequestFinished, base::Unretained(this),
+        params, std::move(callback), verify_result, out_req);
+    return upstream_->Verify(params, verify_result, std::move(callback2), out_req, net_log);
+  }
+
+
+  void SetConfig(const Config& config) override {
+    upstream_->SetConfig(config);
+  }
+
+  void AddObserver(CertVerifier::Observer* observer) override {
+    upstream_->AddObserver(observer);
+  }
+
+  void RemoveObserver(CertVerifier::Observer* observer) override {
+    upstream_->RemoveObserver(observer);
+  }
+
+  void OnRequestFinished(const RequestParams& params,
+                         net::CompletionOnceCallback callback,
+                         net::CertVerifyResult* verify_result,
+                         std::unique_ptr<CertVerifier::Request>* out_req,
+                         int error) {
+    if (client_.is_bound()) {
+      // We take a weak pointer to the request because deletion of the request
+      // is what signals cancellation. Thus if the request is cancelled, the
+      // callback won't be called, thus avoiding UAF, because |verify_result|
+      // is freed when the request is cancelled.
+      *out_req = std::make_unique<Request>();
+      base::WeakPtr<Request> weak_req = static_cast<Request*>(out_req->get())->GetWeakPtr();
+      client_->Verify(error, *verify_result, params.certificate(),
+          params.hostname(), params.flags(), params.ocsp_response(),
+          base::BindOnce(&Request::OnRemoteResponse,
+            weak_req, params, verify_result, error, std::move(callback)));
+    } else {
+      std::move(callback).Run(error);
+    }
+  }
+
+ private:
+  std::unique_ptr<net::CertVerifier> upstream_;
+  mojo::Remote<mojom::CertVerifierClient> client_;
+};
+
 constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess;
 
 NetworkContext::NetworkContextHttpAuthPreferences::
@@ -878,6 +976,13 @@ void NetworkContext::SetClient(
   client_.Bind(std::move(client));
 }
 
+void NetworkContext::SetCertVerifierClient(
+    mojo::PendingRemote<mojom::CertVerifierClient> client) {
+  if (remote_cert_verifier_) {
+    remote_cert_verifier_->Bind(std::move(client));
+  }
+}
+
 void NetworkContext::CreateURLLoaderFactory(
     mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
     mojom::URLLoaderFactoryParamsPtr params) {
@@ -2327,6 +2432,9 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext(
         std::move(cert_verifier));
     cert_verifier = base::WrapUnique(cert_verifier_with_trust_anchors_.get());
 #endif  // BUILDFLAG(IS_CHROMEOS)
+    auto remote_cert_verifier = std::make_unique<RemoteCertVerifier>(std::move(cert_verifier));
+    remote_cert_verifier_ = remote_cert_verifier.get();
+    cert_verifier = std::make_unique<net::CachingCertVerifier>(std::move(remote_cert_verifier));
   }
 
   builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier(
diff --git a/services/network/network_context.h b/services/network/network_context.h
index ebadb14137f7d3bc2a30d050dceaf90645a40e70..453ab6cf11c5fd5aea9f8e30f1d2582cd20f36c4 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -113,6 +113,7 @@ class URLMatcher;
 
 namespace network {
 class CertVerifierWithTrustAnchors;
+class RemoteCertVerifier;
 class CookieManager;
 class HostResolver;
 class MdnsResponderManager;
@@ -245,6 +246,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
   void CreateURLLoaderFactory(
       mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
       mojom::URLLoaderFactoryParamsPtr params) override;
+  void SetCertVerifierClient(
+      mojo::PendingRemote<mojom::CertVerifierClient> client) override;
   void ResetURLLoaderFactories() override;
   void GetViaObliviousHttp(
       mojom::ObliviousHttpRequestPtr request,
@@ -880,6 +883,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
   std::vector<base::OnceClosure> dismount_closures_;
 #endif  // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED)
 
+  raw_ptr<RemoteCertVerifier> remote_cert_verifier_ = nullptr;
+
   // Created on-demand. Null if unused.
   std::unique_ptr<HostResolver> internal_host_resolver_;
   // Map values set to non-null only if that HostResolver has its own private
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 3096bd7d6719b99818ad2e5befefd4dbeee0ddb8..fee16e02e0160825e44c668672176546abe7d99d 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -297,6 +297,16 @@ struct SocketBrokerRemotes {
   pending_remote<SocketBroker> server;
 };
 
+interface CertVerifierClient {
+  Verify(
+    int32 default_error,
+    CertVerifyResult default_result,
+    X509Certificate certificate,
+    string hostname,
+    int32 flags,
+    string? ocsp_response
+  ) => (int32 error_code, CertVerifyResult result);
+};
 
 // Parameters for constructing a network context.
 struct NetworkContextParams {
@@ -970,6 +980,9 @@ interface NetworkContext {
   // Sets a client for this network context.
   SetClient(pending_remote<NetworkContextClient> client);
 
+  // Sets a certificate verifier client for this network context.
+  SetCertVerifierClient(pending_remote<CertVerifierClient>? client);
+
   // Creates a new URLLoaderFactory with the given |params|.
   CreateURLLoaderFactory(pending_receiver<URLLoaderFactory> url_loader_factory,
                          URLLoaderFactoryParams params);
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index caf25a79cface282294e4646dcb8fd08d6ca6efd..381c00fbe1af5711860b3237be1165e1c58540c0 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -62,6 +62,8 @@ class TestNetworkContext : public mojom::NetworkContext {
   void CreateURLLoaderFactory(
       mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
       mojom::URLLoaderFactoryParamsPtr params) override {}
+  void SetCertVerifierClient(
+      mojo::PendingRemote<mojom::CertVerifierClient> client) override {}
   void GetCookieManager(
       mojo::PendingReceiver<mojom::CookieManager> cookie_manager) override {}
   void GetRestrictedCookieManager(
