Bug 1577643 - Implement a security info class for the quic transport. r=keeler
☠☠ backed out by 7d339b451192 ☠ ☠
authorDragana Damjanovic <dd.mozilla@gmail.com>
Tue, 24 Sep 2019 20:56:39 +0000
changeset 494846 48ce2b670f32691b0537c13c165ec8459d74907c
parent 494845 2a39731bf265d0340924be02ade31b9d2ddfff8f
child 494847 2c1a726f2190604ea3d1e37f339b48118d3d19ac
push id114131
push userdluca@mozilla.com
push dateThu, 26 Sep 2019 09:47:34 +0000
treeherdermozilla-inbound@1dc1a755079a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1577643
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1577643 - Implement a security info class for the quic transport. r=keeler Differential Revision: https://phabricator.services.mozilla.com/D44073
netwerk/protocol/http/QuicSocketControl.cpp
netwerk/protocol/http/QuicSocketControl.h
netwerk/protocol/http/moz.build
security/manager/ssl/CommonSocketControl.cpp
security/manager/ssl/CommonSocketControl.h
security/manager/ssl/TransportSecurityInfo.h
security/manager/ssl/moz.build
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsNSSCallbacks.h
security/manager/ssl/nsNSSIOLayer.cpp
security/manager/ssl/nsNSSIOLayer.h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/QuicSocketControl.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "QuicSocketControl.h"
+#include "SharedCertVerifier.h"
+#include "nsNSSComponent.h"
+#include "nsWeakReference.h"
+#include "sslt.h"
+#include "ssl.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS_INHERITED(QuicSocketControl, TransportSecurityInfo,
+                            nsISSLSocketControl, QuicSocketControl)
+
+QuicSocketControl::QuicSocketControl(uint32_t aProviderFlags)
+    : CommonSocketControl(aProviderFlags) {}
+
+void QuicSocketControl::SetCertVerificationResult(PRErrorCode errorCode) {
+  if (errorCode) {
+    mFailedVerification = true;
+    SetCanceled(errorCode);
+  }
+
+  if (OnSocketThread()) {
+    CallAuthenticated();
+  } else {
+    DebugOnly<nsresult> rv = gSocketTransportService->Dispatch(
+        NewRunnableMethod("QuicSocketControl::CallAuthenticated", this,
+                          &QuicSocketControl::CallAuthenticated),
+        NS_DISPATCH_NORMAL);
+  }
+}
+
+NS_IMETHODIMP
+QuicSocketControl::GetSSLVersionOffered(int16_t* aSSLVersionOffered) {
+  *aSSLVersionOffered = nsISSLSocketControl::TLS_VERSION_1_3;
+  return NS_OK;
+}
+
+void QuicSocketControl::CallAuthenticated() {
+  //  // Will be added when Http3 lands
+  /*  if (mHttp3Session) {
+    RefPtr<Http3Session> http3Session = do_QueryReferent(mHttp3Session);
+    http3Session->Authenticated(GetErrorCode());
+  }
+  mHttp3Session = nullptr;*/
+}
+
+// Will be added when Http3 lands
+// void QuicSocketControl::SetAuthenticationCallback(Http3Session
+// *aHttp3Session) {
+//  mHttp3Session = do_GetWeakReference(
+//      static_cast<nsISupportsWeakReference*>(aHttp3Session));
+//}
+
+void QuicSocketControl::HandshakeCompleted() {
+  psm::RememberCertErrorsTable::GetInstance().LookupCertErrorBits(this);
+
+  uint32_t state = nsIWebProgressListener::STATE_IS_SECURE;
+
+  bool distrustImminent;
+  nsresult srv =
+      IsCertificateDistrustImminent(mSucceededCertChain, distrustImminent);
+  if (NS_SUCCEEDED(srv) && distrustImminent) {
+    state |= nsIWebProgressListener::STATE_CERT_DISTRUST_IMMINENT;
+  }
+
+  // If we're here, the TLS handshake has succeeded. Thus if any of these
+  // booleans are true, the user has added an override for a certificate error.
+  if (mIsDomainMismatch || mIsUntrusted || mIsNotValidAtThisTime) {
+    state |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
+  }
+
+  SetSecurityState(state);
+  mHandshakeCompleted = true;
+}
+
+void QuicSocketControl::SetNegotiatedNPN(const nsACString& aValue) {
+  mNegotiatedNPN = aValue;
+  mNPNCompleted = true;
+}
+
+void QuicSocketControl::SetInfo(uint16_t aCipherSuite,
+                                         uint16_t aProtocolVersion,
+                                         uint16_t aKeaGroup,
+                                   uint16_t aSignatureScheme) {
+  SSLCipherSuiteInfo cipherInfo;
+  if (SSL_GetCipherSuiteInfo(aCipherSuite, &cipherInfo, sizeof cipherInfo) ==
+      SECSuccess) {
+    mHaveCipherSuiteAndProtocol = true;
+    mCipherSuite = aCipherSuite;
+    mProtocolVersion = aProtocolVersion & 0xFF;
+    mKeaGroup = getKeaGroupName(aKeaGroup);
+    mSignatureSchemeName = getSignatureName(aSignatureScheme);
+  }
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/QuicSocketControl.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef QuicSocketControl_h
+#define QuicSocketControl_h
+
+#include "CommonSocketControl.h"
+
+namespace mozilla {
+namespace net {
+
+// Will be added when Http3 lands.
+// class Http3Session;
+
+// IID for the QuicSocketControl interface
+#define NS_QUICSOCKETCONTROL_IID                     \
+  {                                                  \
+    0xdbc67fd0, 0x1ac6, 0x457b, {                    \
+      0x91, 0x4e, 0x4c, 0x86, 0x60, 0xff, 0x00, 0x69 \
+    }                                                \
+  }
+
+class QuicSocketControl final : public CommonSocketControl {
+ public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_QUICSOCKETCONTROL_IID)
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_IMETHOD GetSSLVersionOffered(int16_t* aSSLVersionOffered) override;
+
+  explicit QuicSocketControl(uint32_t providerFlags);
+
+  void SetNegotiatedNPN(const nsACString& aValue);
+  void SetInfo(uint16_t aCipherSuite, uint16_t aProtocolVersion,
+               uint16_t aKeaGroup, uint16_t aSignatureScheme);
+
+  // Will be added when Http3 lands.
+  // void SetAuthenticationCallback(Http3Session *aHttp3Session);
+  void CallAuthenticated();
+
+  void HandshakeCompleted();
+  void SetCertVerificationResult(PRErrorCode errorCode) override;
+
+ private:
+  ~QuicSocketControl() = default;
+
+  // For Authentication done callback.
+  nsWeakPtr mHttp3Session;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(QuicSocketControl, NS_QUICSOCKETCONTROL_IID)
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // QuicSocketControl_h
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -103,16 +103,17 @@ UNIFIED_SOURCES += [
     'nsHttpNTLMAuth.cpp',
     'nsHttpRequestHead.cpp',
     'nsHttpResponseHead.cpp',
     'nsHttpTransaction.cpp',
     'nsServerTiming.cpp',
     'NullHttpChannel.cpp',
     'NullHttpTransaction.cpp',
     'ParentChannelListener.cpp',
+    'QuicSocketControl.cpp',
     'TunnelUtils.cpp',
 ]
 
 # These files cannot be built in unified mode because of OS X headers.
 SOURCES += [
     'nsHttpHandler.cpp',
 ]
 
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/CommonSocketControl.cpp
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CommonSocketControl.h"
+#include "SharedCertVerifier.h"
+#include "nsNSSComponent.h"
+#include "sslt.h"
+#include "ssl.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS_INHERITED(CommonSocketControl, TransportSecurityInfo,
+                            nsISSLSocketControl)
+
+CommonSocketControl::CommonSocketControl(uint32_t aProviderFlags)
+    : mNPNCompleted(false),
+      mHandshakeCompleted(false),
+      mJoined(false),
+      mSentClientCert(false),
+      mFailedVerification(false),
+      mResumed(false),
+      mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
+      mProviderFlags(aProviderFlags) {}
+
+NS_IMETHODIMP
+CommonSocketControl::GetNotificationCallbacks(
+    nsIInterfaceRequestor** aCallbacks) {
+  *aCallbacks = mCallbacks;
+  NS_IF_ADDREF(*aCallbacks);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::SetNotificationCallbacks(
+    nsIInterfaceRequestor* aCallbacks) {
+  mCallbacks = aCallbacks;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::ProxyStartSSL(void) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::StartTLS(void) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::SetNPNList(nsTArray<nsCString> & aNPNList) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetNegotiatedNPN(nsACString& aNegotiatedNPN) {
+  if (!mNPNCompleted) {
+      return NS_ERROR_NOT_CONNECTED;
+  }
+
+  aNegotiatedNPN = mNegotiatedNPN;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetAlpnEarlySelection(nsACString& _retval) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetEarlyDataAccepted(bool *aEarlyDataAccepted) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::DriveHandshake(void) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::JoinConnection(const nsACString& npnProtocol,
+                                    const nsACString& hostname, int32_t port,
+                                    bool* _retval) {
+  nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval);
+  if (NS_SUCCEEDED(rv) && *_retval) {
+    // All tests pass - this is joinable
+    mJoined = true;
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::TestJoinConnection(const nsACString& npnProtocol,
+                                        const nsACString& hostname,
+                                        int32_t port, bool* _retval) {
+  *_retval = false;
+
+  // Different ports may not be joined together
+  if (port != GetPort()) return NS_OK;
+
+  // Make sure NPN has been completed and matches requested npnProtocol
+  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol)) return NS_OK;
+
+  IsAcceptableForHost(hostname, _retval);  // sets _retval
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::IsAcceptableForHost(const nsACString& hostname,
+                                         bool* _retval) {
+  NS_ENSURE_ARG(_retval);
+
+  *_retval = false;
+
+  // If this is the same hostname then the certicate status does not
+  // need to be considered. They are joinable.
+  if (hostname.Equals(GetHostName())) {
+    *_retval = true;
+    return NS_OK;
+  }
+
+  // Before checking the server certificate we need to make sure the
+  // handshake has completed.
+  if (!mHandshakeCompleted || !HasServerCert()) {
+    return NS_OK;
+  }
+
+  // If the cert has error bits (e.g. it is untrusted) then do not join.
+  // The value of mHaveCertErrorBits is only reliable because we know that
+  // the handshake completed.
+  if (mHaveCertErrorBits) {
+    return NS_OK;
+  }
+
+  // If the connection is using client certificates then do not join
+  // because the user decides on whether to send client certs to hosts on a
+  // per-domain basis.
+  if (mSentClientCert) return NS_OK;
+
+  // Ensure that the server certificate covers the hostname that would
+  // like to join this connection
+
+  UniqueCERTCertificate nssCert;
+
+  nsCOMPtr<nsIX509Cert> cert;
+  if (NS_FAILED(GetServerCert(getter_AddRefs(cert)))) {
+    return NS_OK;
+  }
+  if (cert) {
+    nssCert.reset(cert->GetCert());
+  }
+
+  if (!nssCert) {
+    return NS_OK;
+  }
+
+  // Attempt to verify the joinee's certificate using the joining hostname.
+  // This ensures that any hostname-specific verification logic (e.g. key
+  // pinning) is satisfied by the joinee's certificate chain.
+  // This verification only uses local information; since we're on the network
+  // thread, we would be blocking on ourselves if we attempted any network i/o.
+  // TODO(bug 1056935): The certificate chain built by this verification may be
+  // different than the certificate chain originally built during the joined
+  // connection's TLS handshake. Consequently, we may report a wrong and/or
+  // misleading certificate chain for HTTP transactions coalesced onto this
+  // connection. This may become problematic in the future. For example,
+  // if/when we begin relying on intermediate certificates being stored in the
+  // securityInfo of a cached HTTPS response, that cached certificate chain may
+  // actually be the wrong chain. We should consider having JoinConnection
+  // return the certificate chain built here, so that the calling Necko code
+  // can associate the correct certificate chain with the HTTP transactions it
+  // is trying to join onto this connection.
+  RefPtr<psm::SharedCertVerifier> certVerifier(psm::GetDefaultCertVerifier());
+  if (!certVerifier) {
+    return NS_OK;
+  }
+  psm::CertVerifier::Flags flags = psm::CertVerifier::FLAG_LOCAL_ONLY;
+  UniqueCERTCertList unusedBuiltChain;
+  mozilla::pkix::Result result = certVerifier->VerifySSLServerCert(
+      nssCert,
+      Maybe<nsTArray<uint8_t>>(),  // stapledOCSPResponse
+      Maybe<nsTArray<uint8_t>>(),  // sctsFromTLSExtension
+      mozilla::pkix::Now(),
+      nullptr,  // pinarg
+      hostname, unusedBuiltChain,
+      false,  // save intermediates
+      flags);
+  if (result != mozilla::pkix::Success) {
+    return NS_OK;
+  }
+
+  // All tests pass
+  *_retval = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetKEAUsed(int16_t *aKEAUsed) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetKEAKeyBits(uint32_t *aKEAKeyBits) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetProviderFlags(uint32_t* aProviderFlags) {
+  *aProviderFlags = mProviderFlags;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetProviderTlsFlags(uint32_t *aProviderTlsFlags) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetSSLVersionUsed(int16_t* aSSLVersionUsed) {
+  *aSSLVersionUsed = mSSLVersionUsed;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetSSLVersionOffered(int16_t* aSSLVersionOffered) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetMACAlgorithmUsed(int16_t *aMACAlgorithmUsed) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+bool  CommonSocketControl::GetDenyClientCert() { return true; }
+
+void CommonSocketControl::SetDenyClientCert(bool aDenyClientCert) {}
+
+NS_IMETHODIMP
+CommonSocketControl::GetClientCert(nsIX509Cert **aClientCert) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::SetClientCert(nsIX509Cert *aClientCert) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetClientCertSent(bool* arg) {
+  *arg = mSentClientCert;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetFailedVerification(bool* arg) {
+  *arg = mFailedVerification;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetEsniTxt(nsACString& aEsniTxt) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::SetEsniTxt(const nsACString& aEsniTxt) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+CommonSocketControl::GetResumed(bool* aResumed) {
+  *aResumed = mResumed;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/CommonSocketControl.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CommonSocketControl_h
+#define CommonSocketControl_h
+
+#include "nsISSLSocketControl.h"
+#include "TransportSecurityInfo.h"
+
+class CommonSocketControl : public mozilla::psm::TransportSecurityInfo,
+                            public nsISSLSocketControl {
+ public:
+
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSISSLSOCKETCONTROL
+
+  explicit CommonSocketControl(uint32_t providerFlags);
+
+  uint32_t GetProviderFlags() const { return mProviderFlags; }
+  void SetSSLVersionUsed(int16_t version) { mSSLVersionUsed = version; }
+  void SetResumed(bool aResumed) { mResumed = aResumed; }
+
+ protected:
+  ~CommonSocketControl() = default;
+  nsCString mNegotiatedNPN;
+  bool mNPNCompleted;
+  bool mHandshakeCompleted;
+  bool mJoined;
+  bool mSentClientCert;
+  bool mFailedVerification;
+  mozilla::Atomic<bool, mozilla::Relaxed> mResumed;
+  uint16_t mSSLVersionUsed;
+  uint32_t mProviderFlags;
+};
+
+#endif  // CommonSocketControl_h
--- a/security/manager/ssl/TransportSecurityInfo.h
+++ b/security/manager/ssl/TransportSecurityInfo.h
@@ -108,28 +108,28 @@ class TransportSecurityInfo : public nsI
   // non-zero mErrorCode, which can only be the case if SetCanceled was called
   // on the original TransportSecurityInfo).
   Atomic<bool> mCanceled;
 
   mutable ::mozilla::Mutex mMutex;
 
  protected:
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+  nsCOMPtr<nsIX509CertList> mSucceededCertChain;
 
  private:
   uint32_t mSecurityState;
 
   PRErrorCode mErrorCode;
 
   int32_t mPort;
   nsCString mHostName;
   OriginAttributes mOriginAttributes;
 
   nsCOMPtr<nsIX509Cert> mServerCert;
-  nsCOMPtr<nsIX509CertList> mSucceededCertChain;
 
   /* Peer cert chain for failed connections (for error reporting) */
   nsCOMPtr<nsIX509CertList> mFailedCertChain;
 
   nsresult ReadSSLStatus(nsIObjectInputStream* aStream);
 };
 
 class RememberCertErrorsTable {
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -54,31 +54,33 @@ XPCOM_MANIFESTS += [
 
 EXTRA_JS_MODULES.psm += [
     'DER.jsm',
     'RemoteSecuritySettings.jsm',
     'X509.jsm',
 ]
 
 EXPORTS += [
+    'CommonSocketControl.h',
     'CryptoTask.h',
     'EnterpriseRoots.h',
     'nsClientAuthRemember.h',
     'nsNSSCallbacks.h',
     'nsNSSCertificate.h',
     'nsNSSComponent.h',
     'nsNSSHelper.h',
     'nsRandomGenerator.h',
     'nsSecurityHeaderParser.h',
     'NSSErrorsService.h',
     'nsSSLSocketProvider.h',
     'nsTLSSocketProvider.h',
     'RootCertificateTelemetryUtils.h',
     'ScopedNSSTypes.h',
     'SharedCertVerifier.h',
+    'TransportSecurityInfo.h',
 ]
 
 EXPORTS.mozilla += [
     'DataStorage.h',
     'DataStorageList.h',
     'PublicSSL.h',
 ]
 
@@ -87,16 +89,17 @@ EXPORTS.mozilla.psm += [
     'TransportSecurityInfo.h',
 ]
 
 EXPORTS.ipc += [
     'DataStorageIPCUtils.h',
 ]
 
 UNIFIED_SOURCES += [
+    'CommonSocketControl.cpp',
     'ContentSignatureVerifier.cpp',
     'CryptoTask.cpp',
     'CSTrustDomain.cpp',
     'DataStorage.cpp',
     'EnterpriseRoots.cpp',
     'LocalCertService.cpp',
     'nsCertOverrideService.cpp',
     'nsClientAuthRemember.cpp',
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -589,17 +589,17 @@ void PK11PasswordPromptRunnable::RunOnTa
 
 char* PK11PasswordPrompt(PK11SlotInfo* slot, PRBool /*retry*/, void* arg) {
   RefPtr<PK11PasswordPromptRunnable> runnable(new PK11PasswordPromptRunnable(
       slot, static_cast<nsIInterfaceRequestor*>(arg)));
   runnable->DispatchToMainThreadAndWait();
   return runnable->mResult;
 }
 
-static nsCString getKeaGroupName(uint32_t aKeaGroup) {
+nsCString getKeaGroupName(uint32_t aKeaGroup) {
   nsCString groupName;
   switch (aKeaGroup) {
     case ssl_grp_ec_secp256r1:
       groupName = NS_LITERAL_CSTRING("P256");
       break;
     case ssl_grp_ec_secp384r1:
       groupName = NS_LITERAL_CSTRING("P384");
       break;
@@ -626,17 +626,17 @@ static nsCString getKeaGroupName(uint32_
     default:
       // This really shouldn't happen!
       MOZ_ASSERT_UNREACHABLE("Invalid key exchange group.");
       groupName = NS_LITERAL_CSTRING("unknown group");
   }
   return groupName;
 }
 
-static nsCString getSignatureName(uint32_t aSignatureScheme) {
+nsCString getSignatureName(uint32_t aSignatureScheme) {
   nsCString signatureName;
   switch (aSignatureScheme) {
     case ssl_sig_none:
       signatureName = NS_LITERAL_CSTRING("none");
       break;
     case ssl_sig_rsa_pkcs1_sha1:
       signatureName = NS_LITERAL_CSTRING("RSA-PKCS1-SHA1");
       break;
@@ -1102,18 +1102,18 @@ static void RebuildVerifiedCertificateIn
   }
 
   if (rv == Success) {
     infoObject->SetCertificateTransparencyInfo(certificateTransparencyInfo);
     infoObject->SetSucceededCertChain(std::move(builtChain));
   }
 }
 
-static nsresult IsCertificateDistrustImminent(nsIX509CertList* aCertList,
-                                              /* out */ bool& isDistrusted) {
+nsresult IsCertificateDistrustImminent(nsIX509CertList* aCertList,
+                                       /* out */ bool& isDistrusted) {
   if (!aCertList) {
     return NS_ERROR_INVALID_POINTER;
   }
 
   nsCOMPtr<nsIX509Cert> rootCert;
   nsCOMPtr<nsIX509CertList> intCerts;
   nsCOMPtr<nsIX509Cert> eeCert;
 
--- a/security/manager/ssl/nsNSSCallbacks.h
+++ b/security/manager/ssl/nsNSSCallbacks.h
@@ -17,22 +17,28 @@
 #include "mozpkix/pkix.h"
 #include "mozpkix/pkixtypes.h"
 
 using mozilla::OriginAttributes;
 using mozilla::TimeDuration;
 using mozilla::Vector;
 
 class nsILoadGroup;
+class nsIX509CertList;
 
 char* PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg);
 
 void HandshakeCallback(PRFileDesc* fd, void* client_data);
 SECStatus CanFalseStartCallback(PRFileDesc* fd, void* client_data,
                                 PRBool* canFalseStart);
 
 mozilla::pkix::Result DoOCSPRequest(
     const nsCString& aiaLocation, const OriginAttributes& originAttributes,
     uint8_t (&ocspRequest)[mozilla::pkix::OCSP_REQUEST_MAX_LENGTH],
     size_t ocspRequestLength, TimeDuration timeout,
     /*out*/ Vector<uint8_t>& result);
 
+nsCString getKeaGroupName(uint32_t aKeaGroup);
+nsCString getSignatureName(uint32_t aSignatureScheme);
+nsresult IsCertificateDistrustImminent(nsIX509CertList* aCertList,
+                                       /* out */ bool& isDistrusted);
+
 #endif  // nsNSSCallbacks_h
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -106,62 +106,49 @@ void getSiteKey(const nsACString& hostNa
 }
 
 }  // unnamed namespace
 
 extern LazyLogModule gPIPNSSLog;
 
 nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags,
                                  uint32_t providerTlsFlags)
-    : mFd(nullptr),
+    : CommonSocketControl(providerFlags),
+      mFd(nullptr),
       mCertVerificationState(before_cert_verification),
       mSharedState(aState),
       mForSTARTTLS(false),
       mHandshakePending(true),
       mPreliminaryHandshakeDone(false),
-      mNPNCompleted(false),
       mEarlyDataAccepted(false),
       mDenyClientCert(false),
       mFalseStartCallbackCalled(false),
       mFalseStarted(false),
       mIsFullHandshake(false),
-      mHandshakeCompleted(false),
-      mJoined(false),
-      mSentClientCert(false),
       mNotedTimeUntilReady(false),
-      mFailedVerification(false),
-      mResumed(false),
       mIsShortWritePending(false),
       mShortWritePendingByte(0),
       mShortWriteOriginalAmount(-1),
       mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
       mKEAKeyBits(0),
-      mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
       mMACAlgorithmUsed(nsISSLSocketControl::SSL_MAC_UNKNOWN),
-      mProviderFlags(providerFlags),
       mProviderTlsFlags(providerTlsFlags),
       mSocketCreationTimestamp(TimeStamp::Now()),
       mPlaintextBytesRead(0),
       mClientCert(nullptr) {
   mTLSVersionRange.min = 0;
   mTLSVersionRange.max = 0;
 }
 
 nsNSSSocketInfo::~nsNSSSocketInfo() {}
 
 NS_IMPL_ISUPPORTS_INHERITED(nsNSSSocketInfo, TransportSecurityInfo,
                             nsISSLSocketControl)
 
 NS_IMETHODIMP
-nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags) {
-  *aProviderFlags = mProviderFlags;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNSSSocketInfo::GetProviderTlsFlags(uint32_t* aProviderTlsFlags) {
   *aProviderTlsFlags = mProviderTlsFlags;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::GetKEAUsed(int16_t* aKea) {
   *aKea = mKEAUsed;
@@ -170,22 +157,16 @@ nsNSSSocketInfo::GetKEAUsed(int16_t* aKe
 
 NS_IMETHODIMP
 nsNSSSocketInfo::GetKEAKeyBits(uint32_t* aKeyBits) {
   *aKeyBits = mKEAKeyBits;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed) {
-  *aSSLVersionUsed = mSSLVersionUsed;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNSSSocketInfo::GetSSLVersionOffered(int16_t* aSSLVersionOffered) {
   *aSSLVersionOffered = mTLSVersionRange.max;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::GetMACAlgorithmUsed(int16_t* aMac) {
   *aMac = mMACAlgorithmUsed;
@@ -201,41 +182,16 @@ nsNSSSocketInfo::GetClientCert(nsIX509Ce
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::SetClientCert(nsIX509Cert* aClientCert) {
   mClientCert = aClientCert;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsNSSSocketInfo::GetClientCertSent(bool* arg) {
-  *arg = mSentClientCert;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSSocketInfo::GetFailedVerification(bool* arg) {
-  *arg = mFailedVerification;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSSocketInfo::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
-  nsCOMPtr<nsIInterfaceRequestor> ir(mCallbacks);
-  ir.forget(aCallbacks);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
-  mCallbacks = aCallbacks;
-  return NS_OK;
-}
-
 void nsNSSSocketInfo::NoteTimeUntilReady() {
   if (mNotedTimeUntilReady) return;
 
   mNotedTimeUntilReady = true;
 
   // This will include TCP and proxy tunnel wait time
   Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
                                  mSocketCreationTimestamp, TimeStamp::Now());
@@ -298,24 +254,16 @@ void nsNSSSocketInfo::SetNegotiatedNPN(c
     mNegotiatedNPN.Truncate();
   } else {
     mNegotiatedNPN.Assign(value, length);
   }
   mNPNCompleted = true;
 }
 
 NS_IMETHODIMP
-nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN) {
-  if (!mNPNCompleted) return NS_ERROR_NOT_CONNECTED;
-
-  aNegotiatedNPN = mNegotiatedNPN;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNSSSocketInfo::GetAlpnEarlySelection(nsACString& aAlpnSelected) {
   aAlpnSelected.Truncate();
 
   SSLPreliminaryChannelInfo info;
   SECStatus rv = SSL_GetPreliminaryChannelInfo(mFd, &info, sizeof(info));
   if (rv != SECSuccess || !info.canSendEarlyData) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -343,24 +291,16 @@ nsNSSSocketInfo::GetEarlyDataAccepted(bo
   *aAccepted = mEarlyDataAccepted;
   return NS_OK;
 }
 
 void nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted) {
   mEarlyDataAccepted = aAccepted;
 }
 
-NS_IMETHODIMP
-nsNSSSocketInfo::GetResumed(bool* aResumed) {
-  *aResumed = mResumed;
-  return NS_OK;
-}
-
-void nsNSSSocketInfo::SetResumed(bool aResumed) { mResumed = aResumed; }
-
 bool nsNSSSocketInfo::GetDenyClientCert() { return mDenyClientCert; }
 
 void nsNSSSocketInfo::SetDenyClientCert(bool aDenyClientCert) {
   mDenyClientCert = aDenyClientCert;
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::DriveHandshake() {
@@ -381,133 +321,16 @@ nsNSSSocketInfo::DriveHandshake() {
     }
 
     SetCanceled(errorCode);
     return GetXPCOMFromNSSError(errorCode);
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname,
-                                     bool* _retval) {
-  NS_ENSURE_ARG(_retval);
-
-  *_retval = false;
-
-  // If this is the same hostname then the certicate status does not
-  // need to be considered. They are joinable.
-  if (hostname.Equals(GetHostName())) {
-    *_retval = true;
-    return NS_OK;
-  }
-
-  // Before checking the server certificate we need to make sure the
-  // handshake has completed.
-  if (!mHandshakeCompleted || !HasServerCert()) {
-    return NS_OK;
-  }
-
-  // If the cert has error bits (e.g. it is untrusted) then do not join.
-  // The value of mHaveCertErrorBits is only reliable because we know that
-  // the handshake completed.
-  if (mHaveCertErrorBits) {
-    return NS_OK;
-  }
-
-  // If the connection is using client certificates then do not join
-  // because the user decides on whether to send client certs to hosts on a
-  // per-domain basis.
-  if (mSentClientCert) return NS_OK;
-
-  // Ensure that the server certificate covers the hostname that would
-  // like to join this connection
-
-  UniqueCERTCertificate nssCert;
-
-  nsCOMPtr<nsIX509Cert> cert;
-  if (NS_FAILED(GetServerCert(getter_AddRefs(cert)))) {
-    return NS_OK;
-  }
-  if (cert) {
-    nssCert.reset(cert->GetCert());
-  }
-
-  if (!nssCert) {
-    return NS_OK;
-  }
-
-  // Attempt to verify the joinee's certificate using the joining hostname.
-  // This ensures that any hostname-specific verification logic (e.g. key
-  // pinning) is satisfied by the joinee's certificate chain.
-  // This verification only uses local information; since we're on the network
-  // thread, we would be blocking on ourselves if we attempted any network i/o.
-  // TODO(bug 1056935): The certificate chain built by this verification may be
-  // different than the certificate chain originally built during the joined
-  // connection's TLS handshake. Consequently, we may report a wrong and/or
-  // misleading certificate chain for HTTP transactions coalesced onto this
-  // connection. This may become problematic in the future. For example,
-  // if/when we begin relying on intermediate certificates being stored in the
-  // securityInfo of a cached HTTPS response, that cached certificate chain may
-  // actually be the wrong chain. We should consider having JoinConnection
-  // return the certificate chain built here, so that the calling Necko code
-  // can associate the correct certificate chain with the HTTP transactions it
-  // is trying to join onto this connection.
-  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
-  if (!certVerifier) {
-    return NS_OK;
-  }
-  CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY;
-  UniqueCERTCertList unusedBuiltChain;
-  mozilla::pkix::Result result = certVerifier->VerifySSLServerCert(
-      nssCert,
-      Maybe<nsTArray<uint8_t>>(),  // stapledOCSPResponse
-      Maybe<nsTArray<uint8_t>>(),  // sctsFromTLSExtension
-      mozilla::pkix::Now(),
-      nullptr,  // pinarg
-      hostname, unusedBuiltChain,
-      false,  // save intermediates
-      flags);
-  if (result != mozilla::pkix::Success) {
-    return NS_OK;
-  }
-
-  // All tests pass
-  *_retval = true;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSSocketInfo::TestJoinConnection(const nsACString& npnProtocol,
-                                    const nsACString& hostname, int32_t port,
-                                    bool* _retval) {
-  *_retval = false;
-
-  // Different ports may not be joined together
-  if (port != GetPort()) return NS_OK;
-
-  // Make sure NPN has been completed and matches requested npnProtocol
-  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol)) return NS_OK;
-
-  IsAcceptableForHost(hostname, _retval);  // sets _retval
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
-                                const nsACString& hostname, int32_t port,
-                                bool* _retval) {
-  nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval);
-  if (NS_SUCCEEDED(rv) && *_retval) {
-    // All tests pass - this is joinable
-    mJoined = true;
-  }
-  return rv;
-}
-
 bool nsNSSSocketInfo::GetForSTARTTLS() { return mForSTARTTLS; }
 
 void nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS) {
   mForSTARTTLS = aForSTARTTLS;
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::ProxyStartSSL() { return ActivateSSL(); }
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -2,17 +2,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsNSSIOLayer_h
 #define nsNSSIOLayer_h
 
-#include "TransportSecurityInfo.h"
+#include "CommonSocketControl.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsDataHashtable.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsIProxyInfo.h"
 #include "nsISSLSocketControl.h"
@@ -26,60 +26,75 @@ namespace psm {
 class SharedSSLState;
 }  // namespace psm
 }  // namespace mozilla
 
 using mozilla::OriginAttributes;
 
 class nsIObserver;
 
-class nsNSSSocketInfo final : public mozilla::psm::TransportSecurityInfo,
-                              public nsISSLSocketControl {
+class nsNSSSocketInfo final : public CommonSocketControl {
  public:
   nsNSSSocketInfo(mozilla::psm::SharedSSLState& aState, uint32_t providerFlags,
                   uint32_t providerTlsFlags);
 
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSISSLSOCKETCONTROL
 
   void SetForSTARTTLS(bool aForSTARTTLS);
   bool GetForSTARTTLS();
 
   nsresult GetFileDescPtr(PRFileDesc** aFilePtr);
   nsresult SetFileDescPtr(PRFileDesc* aFilePtr);
 
   bool IsHandshakePending() const { return mHandshakePending; }
   void SetHandshakeNotPending() { mHandshakePending = false; }
 
   void SetTLSVersionRange(SSLVersionRange range) { mTLSVersionRange = range; }
   SSLVersionRange GetTLSVersionRange() const { return mTLSVersionRange; };
 
+  // From nsISSLSocketControl.
+  NS_IMETHOD ProxyStartSSL(void) override;
+  NS_IMETHOD StartTLS(void) override;
+  NS_IMETHOD SetNPNList(nsTArray<nsCString> & aNPNList) override;
+  NS_IMETHOD GetAlpnEarlySelection(nsACString& _retval) override;
+  NS_IMETHOD GetEarlyDataAccepted(bool *aEarlyDataAccepted) override;
+  NS_IMETHOD DriveHandshake(void) override;
+  using nsISSLSocketControl::GetKEAUsed;
+  NS_IMETHOD GetKEAUsed(int16_t *aKEAUsed) override;
+  NS_IMETHOD GetKEAKeyBits(uint32_t *aKEAKeyBits) override;
+  NS_IMETHOD GetProviderTlsFlags(uint32_t *aProviderTlsFlags) override;
+  NS_IMETHOD GetSSLVersionOffered(int16_t* aSSLVersionOffered) override;
+  NS_IMETHOD GetMACAlgorithmUsed(int16_t *aMACAlgorithmUsed) override;
+  bool GetDenyClientCert() override;
+  void SetDenyClientCert(bool aDenyClientCert) override;
+  NS_IMETHOD GetClientCert(nsIX509Cert **aClientCert) override;
+  NS_IMETHOD SetClientCert(nsIX509Cert *aClientCert) override;
+  NS_IMETHOD GetEsniTxt(nsACString& aEsniTxt) override;
+  NS_IMETHOD SetEsniTxt(const nsACString& aEsniTxt) override;
+
   PRStatus CloseSocketAndDestroy();
 
   void SetNegotiatedNPN(const char* value, uint32_t length);
   void SetEarlyDataAccepted(bool aAccepted);
 
-  void SetResumed(bool aResumed);
-
   void SetHandshakeCompleted();
   bool IsHandshakeCompleted() const { return mHandshakeCompleted; }
   void NoteTimeUntilReady();
 
   void SetFalseStartCallbackCalled() { mFalseStartCallbackCalled = true; }
   void SetFalseStarted() { mFalseStarted = true; }
 
   // Note that this is only valid *during* a handshake; at the end of the
   // handshake, it gets reset back to false.
   void SetFullHandshake() { mIsFullHandshake = true; }
   bool IsFullHandshake() const { return mIsFullHandshake; }
 
   bool GetJoined() { return mJoined; }
   void SetSentClientCert() { mSentClientCert = true; }
 
-  uint32_t GetProviderFlags() const { return mProviderFlags; }
   uint32_t GetProviderTlsFlags() const { return mProviderTlsFlags; }
 
   mozilla::psm::SharedSSLState& SharedState();
 
   // XXX: These are only used on for diagnostic purposes
   enum CertVerificationState {
     before_cert_verification,
     waiting_for_cert_verification,
@@ -97,18 +112,16 @@ class nsNSSSocketInfo final : public moz
 
   bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; }
   void SetPreliminaryHandshakeDone() { mPreliminaryHandshakeDone = true; }
 
   void SetKEAUsed(uint16_t kea) { mKEAUsed = kea; }
 
   void SetKEAKeyBits(uint32_t keaBits) { mKEAKeyBits = keaBits; }
 
-  void SetSSLVersionUsed(int16_t version) { mSSLVersionUsed = version; }
-
   void SetMACAlgorithmUsed(int16_t mac) { mMACAlgorithmUsed = mac; }
 
   void SetShortWritePending(int32_t amount, unsigned char data) {
     mIsShortWritePending = true;
     mShortWriteOriginalAmount = amount;
     mShortWritePendingByte = data;
   }
 
@@ -156,30 +169,23 @@ class nsNSSSocketInfo final : public moz
   mozilla::psm::SharedSSLState& mSharedState;
   bool mForSTARTTLS;
   SSLVersionRange mTLSVersionRange;
   bool mHandshakePending;
   bool mPreliminaryHandshakeDone;  // after false start items are complete
 
   nsresult ActivateSSL();
 
-  nsCString mNegotiatedNPN;
   nsCString mEsniTxt;
-  bool mNPNCompleted;
   bool mEarlyDataAccepted;
   bool mDenyClientCert;
   bool mFalseStartCallbackCalled;
   bool mFalseStarted;
   bool mIsFullHandshake;
-  bool mHandshakeCompleted;
-  bool mJoined;
-  bool mSentClientCert;
   bool mNotedTimeUntilReady;
-  bool mFailedVerification;
-  mozilla::Atomic<bool, mozilla::Relaxed> mResumed;
 
   // True when SSL layer has indicated an "SSL short write", i.e. need
   // to call on send one or more times to push all pending data to write.
   bool mIsShortWritePending;
 
   // These are only valid if mIsShortWritePending is true.
   //
   // Value of the last byte pending from the SSL short write that needs
@@ -193,20 +199,18 @@ class nsNSSSocketInfo final : public moz
 #ifdef DEBUG
   mozilla::UniquePtr<char[]> mShortWriteBufferCheck;
 #endif
 
   // mKEA* are used in false start and http/2 detetermination
   // Values are from nsISSLSocketControl
   int16_t mKEAUsed;
   uint32_t mKEAKeyBits;
-  int16_t mSSLVersionUsed;
   int16_t mMACAlgorithmUsed;
 
-  uint32_t mProviderFlags;
   uint32_t mProviderTlsFlags;
   mozilla::TimeStamp mSocketCreationTimestamp;
   uint64_t mPlaintextBytesRead;
 
   nsCOMPtr<nsIX509Cert> mClientCert;
 
   // if non-null this is a reference to the mSharedState (which is
   // not an owning reference). If this is used, the info has a private