Bug 1439105 - Ban H2 Client Certs Post Handshake r=bagder,keeler
authorPatrick McManus <mcmanus@ducksong.com>
Sat, 17 Feb 2018 08:10:13 -0500
changeset 460291 c9fd03a815bcbce19263597ce97b5ff1898ec2a2
parent 460290 4ebf3007d14ffe8100fd04c1f2fbba134a29a2ce
child 460292 478608ccfa700a6fc46a0125f0bfd789bb4fc8a4
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbagder, keeler
bugs1439105
milestone60.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 1439105 - Ban H2 Client Certs Post Handshake r=bagder,keeler MozReview-Commit-ID: Dfx5bB5NOBp
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/socket/nsISSLSocketControl.idl
security/manager/ssl/nsNSSIOLayer.cpp
security/manager/ssl/nsNSSIOLayer.h
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -271,25 +271,28 @@ nsHttpConnection::Start0RTTSpdy(uint8_t 
              "transactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
         return;
     }
 
     mTransaction = mSpdySession;
 }
 
 void
-nsHttpConnection::StartSpdy(uint8_t spdyVersion)
+nsHttpConnection::StartSpdy(nsISSLSocketControl *sslControl, uint8_t spdyVersion)
 {
     LOG(("nsHttpConnection::StartSpdy [this=%p, mDid0RTTSpdy=%d]\n", this, mDid0RTTSpdy));
 
     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     MOZ_ASSERT(!mSpdySession || mDid0RTTSpdy);
 
     mUsingSpdyVersion = spdyVersion;
     mEverUsedSpdy = true;
+    if (sslControl) {
+        sslControl->SetDenyClientCert(true);
+    }
 
     if (!mDid0RTTSpdy) {
         mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
                                                     false);
     }
 
     if (!mReportedSpdy) {
         mReportedSpdy = true;
@@ -543,28 +546,28 @@ nsHttpConnection::EnsureNPNComplete(nsre
                 mBootstrappedTimings.secureConnectionStart =
                     mTransaction->QueryNullTransaction()->GetSecureConnectionStart();
                 mBootstrappedTimings.tcpConnectEnd =
                     mTransaction->QueryNullTransaction()->GetTcpConnectEnd();
             }
             uint32_t infoIndex;
             const SpdyInformation *info = gHttpHandler->SpdyInfo();
             if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
-                StartSpdy(info->Version[infoIndex]);
+                StartSpdy(ssl, info->Version[infoIndex]);
             }
         } else {
           LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %" PRId64 " bytes "
                "has been sent during 0RTT.", this, mContentBytesWritten0RTT));
           mContentBytesWritten = mContentBytesWritten0RTT;
           if (mSpdySession) {
               // We had already started 0RTT-spdy, now we need to fully set up
               // spdy, since we know we're sticking with it.
               LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - finishing "
                    "StartSpdy for 0rtt spdy session %p", this, mSpdySession.get()));
-              StartSpdy(mSpdySession->SpdyVersion());
+              StartSpdy(ssl, mSpdySession->SpdyVersion());
           }
         }
 
         Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy());
     }
 
 npnComplete:
     LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] setting complete to true", this));
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -267,17 +267,17 @@ private:
 
     // Makes certain the SSL handshake is complete and NPN negotiation
     // has had a chance to happen
     MOZ_MUST_USE bool EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
                                         uint32_t &aOut0RTTBytesWritten);
     void     SetupSSL();
 
     // Start the Spdy transaction handler when NPN indicates spdy/*
-    void     StartSpdy(uint8_t versionLevel);
+    void     StartSpdy(nsISSLSocketControl *ssl, uint8_t versionLevel);
     // Like the above, but do the bare minimum to do 0RTT data, so we can back
     // it out, if necessary
     void     Start0RTTSpdy(uint8_t versionLevel);
 
     // Helpers for Start*Spdy
     nsresult TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> > &list);
     nsresult MoveTransactionsToSpdy(nsresult status, nsTArray<RefPtr<nsAHttpTransaction> > &list);
 
--- a/netwerk/socket/nsISSLSocketControl.idl
+++ b/netwerk/socket/nsISSLSocketControl.idl
@@ -126,16 +126,22 @@ interface nsISSLSocketControl : nsISuppo
     const short SSL_HMAC_MD5    = 3;
     const short SSL_HMAC_SHA    = 4;
     const short SSL_HMAC_SHA256 = 5;
     const short SSL_MAC_AEAD    = 6;
 
     [infallible] readonly attribute short MACAlgorithmUsed;
 
     /**
+     * If set to true before the server requests a client cert
+     * no cert will be sent.
+     */
+    [noscript] attribute boolean denyClientCert;
+
+    /**
      * If set before the server requests a client cert (assuming it does so at
      * all), then this cert will be presented to the server, instead of asking
      * the user or searching the set of rememebered user cert decisions.
      */
     attribute nsIX509Cert clientCert;
 
     /**
      * bypassAuthentication is true if the server certificate checks are
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -115,16 +115,17 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedS
     mCertVerificationState(before_cert_verification),
     mSharedState(aState),
     mForSTARTTLS(false),
     mHandshakePending(true),
     mRememberClientAuthCertificate(false),
     mPreliminaryHandshakeDone(false),
     mNPNCompleted(false),
     mEarlyDataAccepted(false),
+    mDenyClientCert(false),
     mFalseStartCallbackCalled(false),
     mFalseStarted(false),
     mIsFullHandshake(false),
     mHandshakeCompleted(false),
     mJoined(false),
     mSentClientCert(false),
     mNotedTimeUntilReady(false),
     mFailedVerification(false),
@@ -391,16 +392,30 @@ nsNSSSocketInfo::GetEarlyDataAccepted(bo
 
 void
 nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted)
 {
   mEarlyDataAccepted = aAccepted;
 }
 
 NS_IMETHODIMP
+nsNSSSocketInfo::GetDenyClientCert(bool* aDenyClientCert)
+{
+  *aDenyClientCert = mDenyClientCert;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetDenyClientCert(bool aDenyClientCert)
+{
+  mDenyClientCert = aDenyClientCert;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsNSSSocketInfo::DriveHandshake()
 {
   if (!mFd) {
     return NS_ERROR_FAILURE;
   }
   PRErrorCode errorCode = GetErrorCode();
   if (errorCode) {
     return GetXPCOMFromNSSError(errorCode);
@@ -2141,16 +2156,24 @@ nsNSS_SSLGetClientAuthData(void* arg, PR
   UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket));
   if (!serverCert) {
     MOZ_ASSERT_UNREACHABLE(
       "Missing server cert should have been detected during server cert auth.");
     PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
     return SECFailure;
   }
 
+  if (info->GetDenyClientCert()) {
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+           ("[%p] Not returning client cert due to denyClientCert attribute\n", socket));
+    *pRetCert = nullptr;
+    *pRetKey = nullptr;
+    return SECSuccess;
+  }
+
   if (info->GetJoined()) {
     // We refuse to send a client certificate when there are multiple hostnames
     // joined on this connection, because we only show the user one hostname
     // (mHostName) in the client certificate UI.
 
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("[%p] Not returning client cert due to previous join\n", socket));
     *pRetCert = nullptr;
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -68,16 +68,17 @@ public:
   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; }
+  bool GetDenyClientCert() { return mDenyClientCert; }
   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
@@ -180,16 +181,17 @@ private:
   bool mRememberClientAuthCertificate;
   bool mPreliminaryHandshakeDone; // after false start items are complete
 
   nsresult ActivateSSL();
 
   nsCString mNegotiatedNPN;
   bool      mNPNCompleted;
   bool      mEarlyDataAccepted;
+  bool      mDenyClientCert;
   bool      mFalseStartCallbackCalled;
   bool      mFalseStarted;
   bool      mIsFullHandshake;
   bool      mHandshakeCompleted;
   bool      mJoined;
   bool      mSentClientCert;
   bool      mNotedTimeUntilReady;
   bool      mFailedVerification;