bug 528288 spdy - only coalesce (i.e. ip pool) hostnames with compatible ssl certs sr=biesi r=honzab r=bsmith
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 02 Dec 2011 10:28:57 -0500
changeset 81185 a73317c8e854ce930a98c23b3f632af94c81cc00
parent 81184 a267b3c9d217985b9af1a5f962af0db5735acee8
child 81186 56b778efac493e2879ec6bf52e778bb741f1cf30
push id21564
push usermak77@bonardo.net
push dateSat, 03 Dec 2011 11:10:17 +0000
treeherdermozilla-central@a68c96c1d8e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, honzab, bsmith
bugs528288
milestone11.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 528288 spdy - only coalesce (i.e. ip pool) hostnames with compatible ssl certs sr=biesi r=honzab r=bsmith patch 9
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
security/manager/ssl/public/nsIX509Cert3.idl
security/manager/ssl/src/nsNSSCertificate.cpp
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -46,16 +46,19 @@
 
 #include "nsIServiceManager.h"
 
 #include "nsIObserverService.h"
 #include "nsIDNSRecord.h"
 #include "nsIDNSService.h"
 #include "nsICancelable.h"
 
+#include "nsISSLStatusProvider.h"
+#include "nsISSLStatus.h"
+
 using namespace mozilla;
 
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 
 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
 
 //-----------------------------------------------------------------------------
@@ -442,33 +445,70 @@ nsHttpConnectionMgr::ReportSpdyConnectio
     }
     
     ent->mUsingSpdy = true;
 
     PRUint32 ttl = conn->TimeToLive();
     PRUint64 timeOfExpire = NowInSeconds() + ttl;
     if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
         PruneDeadConnectionsAfter(ttl);
-        
-    nsConnectionEntry *preferred = GetSpdyPreferred(ent->mDottedDecimalAddress);
+
+    // Lookup preferred directly from the hash instead of using
+    // GetSpdyPreferred() because we want to avoid the cert compatibility
+    // check at this point because the cert is never part of the hash
+    // lookup. Filtering on that has to be done at the time of use
+    // rather than the time of registration (i.e. now).
+    nsConnectionEntry *preferred =
+        mSpdyPreferredHash.Get(ent->mDottedDecimalAddress);
+
     LOG(("ReportSpdyConnection %s %s ent=%p ispreferred=%d\n",
          ent->mConnInfo->Host(), ent->mDottedDecimalAddress.get(),
          ent, preferred));
     
     if (!preferred) {
         ent->mSpdyPreferred = true;
-        SetSpdyPreferred(ent->mDottedDecimalAddress, ent);
+        SetSpdyPreferred(ent);
         ent->mSpdyRedir = false;
     }
     else if (preferred != ent) {
         // A different hostname is the preferred spdy host for this
         // IP address.
         ent->mSpdyRedir = true;
         conn->DontReuse();
     }
+
+    // If this is a preferred host for coalescing (aka ip pooling) then
+    // keep a reference to the server SSL cert. This will be used as an
+    // extra level of verification when deciding that
+    // connections from other hostnames are redirected to the preferred host.
+    //
+
+    // Even if mCert is already set update the reference in case the
+    // reference is changing.
+    ent->mCert = nsnull;
+
+    nsCOMPtr<nsISupports> securityInfo;
+    nsCOMPtr<nsISSLStatusProvider> sslStatusProvider;
+    nsCOMPtr<nsISSLStatus> sslStatus;
+    nsCOMPtr<nsIX509Cert> cert;
+
+    conn->GetSecurityInfo(getter_AddRefs(securityInfo));
+    if (securityInfo)
+        sslStatusProvider = do_QueryInterface(securityInfo);
+
+    if (sslStatusProvider)
+        sslStatusProvider->
+            GetSSLStatus(getter_AddRefs(sslStatus));
+
+    if (sslStatus)
+        sslStatus->GetServerCert(getter_AddRefs(cert));
+
+    if (cert)
+        ent->mCert = do_QueryInterface(cert);
+
     ProcessSpdyPendingQ();
 }
 
 bool
 nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
 {
     // The Alternate Protocol hash is protected under the monitor because
     // it is read from both the main and the network thread.
@@ -529,37 +569,62 @@ nsHttpConnectionMgr::TrimAlternateProtoc
     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
     
     if (self->mAlternateProtocolHash.mHashTable.entryCount > 2000)
         return PL_DHASH_REMOVE;
     return PL_DHASH_STOP;
 }
 
 nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::GetSpdyPreferred(nsACString &aDottedDecimal)
+nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
 {
     if (!gHttpHandler->IsSpdyEnabled() ||
         !gHttpHandler->CoalesceSpdy() ||
-        aDottedDecimal.IsEmpty())
+        aOriginalEntry->mDottedDecimalAddress.IsEmpty())
         return nsnull;
 
-    return mSpdyPreferredHash.Get(aDottedDecimal);
+    nsConnectionEntry *preferred =
+        mSpdyPreferredHash.Get(aOriginalEntry->mDottedDecimalAddress);
+
+    if (preferred == aOriginalEntry)
+        return aOriginalEntry;   /* no redirection so no cert check required */
+
+    if (!preferred || !preferred->mCert)
+        return nsnull;                         /* no ip pooling */
+
+    nsresult rv;
+    bool validCert = false;
+
+    rv = preferred->mCert->IsValidForHostname(
+        aOriginalEntry->mConnInfo->GetHost(), &validCert);
+
+    if (NS_FAILED(rv) || !validCert) {
+        LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
+             "Host %s has cert which cannot be confirmed to use "
+             "with %s connections",
+             preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
+        return nsnull;                            /* no ip pooling */
+    }
+
+    LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
+         "Host %s has cert valid for %s connections",
+         preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
+    return preferred;
 }
 
 void
-nsHttpConnectionMgr::SetSpdyPreferred(nsACString &aDottedDecimal,
-                                      nsConnectionEntry *ent)
+nsHttpConnectionMgr::SetSpdyPreferred(nsConnectionEntry *ent)
 {
     if (!gHttpHandler->CoalesceSpdy())
         return;
 
-    if (aDottedDecimal.IsEmpty())
+    if (ent->mDottedDecimalAddress.IsEmpty())
         return;
     
-    mSpdyPreferredHash.Put(aDottedDecimal, ent);
+    mSpdyPreferredHash.Put(ent->mDottedDecimalAddress, ent);
 }
 
 void
 nsHttpConnectionMgr::RemoveSpdyPreferred(nsACString &aDottedDecimal)
 {
     if (!gHttpHandler->CoalesceSpdy())
         return;
 
@@ -977,18 +1042,17 @@ nsHttpConnectionMgr::GetConnection(nsCon
         // If this is a possible Spdy connection we need to limit the number of
         // connections outstanding to 1 while we wait for the spdy/https
         // ReportSpdyConnection()
     
         if (gHttpHandler->IsSpdyEnabled() &&
             ent->mConnInfo->UsingSSL() &&
             !ent->mConnInfo->UsingHttpProxy())
         {
-            nsConnectionEntry *preferred =
-                GetSpdyPreferred(ent->mDottedDecimalAddress);
+            nsConnectionEntry *preferred = GetSpdyPreferred(ent);
             if (preferred)
                 ent = preferred;
 
             if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
                 (ent->mSpdyRedir || ent->mHalfOpens.Length() ||
                  ent->mActiveConns.Length()))
                 return;
         }
@@ -1195,18 +1259,17 @@ nsHttpConnectionMgr::ProcessNewTransacti
         ent = new nsConnectionEntry(clone);
         if (!ent)
             return NS_ERROR_OUT_OF_MEMORY;
         mCT.Put(ci->HashKey(), ent);
     }
 
     // SPDY coalescing of hostnames means we might redirect from this
     // connection entry onto the preferred one.
-    nsConnectionEntry *preferredEntry =
-        GetSpdyPreferred(ent->mDottedDecimalAddress);
+    nsConnectionEntry *preferredEntry = GetSpdyPreferred(ent);
     if (preferredEntry) {
         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
              "redirected via coalescing from %s to %s\n", trans,
              ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
         ent = preferredEntry;
     }
 
     // If we are doing a force reload then close out any existing conns
@@ -1289,17 +1352,17 @@ nsHttpConnectionMgr::ProcessSpdyPendingQ
 }
 
 nsHttpConnection *
 nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ABORT_IF_FALSE(ent, "no connection entry");
 
-    nsConnectionEntry *preferred = GetSpdyPreferred(ent->mDottedDecimalAddress);
+    nsConnectionEntry *preferred = GetSpdyPreferred(ent);
 
     // this entry is spdy-enabled if it is a redirect to another spdy host
     if (preferred && preferred != ent) {
         ent->mUsingSpdy = true;
         ent->mSpdyRedir = true;
     }
     else {
         ent->mSpdyRedir = false;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -49,16 +49,17 @@
 #include "nsAutoPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsISocketTransportService.h"
 #include "nsIDNSListener.h"
 #include "nsHashSets.h"
 
 #include "nsIObserver.h"
 #include "nsITimer.h"
+#include "nsIX509Cert3.h"
 
 class nsHttpPipeline;
 
 //-----------------------------------------------------------------------------
 
 class nsHttpConnectionMgr : public nsIObserver
 {
 public:
@@ -200,16 +201,17 @@ private:
         //
         nsCString mDottedDecimalAddress;
 
         bool mUsingSpdy;
         bool mTestedSpdy;
         bool mSpdyRedir;
         bool mDidDNS;
         bool mSpdyPreferred;
+        nsCOMPtr<nsIX509Cert3> mCert;
     };
 
     // nsConnectionHandle
     //
     // thin wrapper around a real connection, used to keep track of references
     // to the connection to determine when the connection may be reused.  the
     // transaction (or pipeline) owns a reference to this handle.  this extra
     // layer of indirection greatly simplifies consumer code, avoiding the
@@ -316,19 +318,18 @@ private:
     nsresult EnsureSocketThreadTargetIfOnline();
     void     ClosePersistentConnections(nsConnectionEntry *ent);
     nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     StartedConnect();
     void     RecvdConnect();
 
     // Manage the preferred spdy connection entry for this address
-    nsConnectionEntry *GetSpdyPreferred(nsACString &aDottedDecimal);
-    void               SetSpdyPreferred(nsACString &aDottedDecimal,
-                                        nsConnectionEntry *ent);
+    nsConnectionEntry *GetSpdyPreferred(nsConnectionEntry *aOriginalEntry);
+    void               SetSpdyPreferred(nsConnectionEntry *ent);
     void               RemoveSpdyPreferred(nsACString &aDottedDecimal);
     nsHttpConnection  *GetSpdyPreferredConn(nsConnectionEntry *ent);
     nsDataHashtable<nsCStringHashKey, nsConnectionEntry *>   mSpdyPreferredHash;
 
     void               ProcessSpdyPendingQ(nsConnectionEntry *ent);
     void               ProcessSpdyPendingQ();
     static PLDHashOperator ProcessSpdyPendingQCB(
         const nsACString &key, nsAutoPtr<nsConnectionEntry> &ent,
--- a/security/manager/ssl/public/nsIX509Cert3.idl
+++ b/security/manager/ssl/public/nsIX509Cert3.idl
@@ -38,17 +38,17 @@
 
 #include "nsIX509Cert2.idl"
 
 interface nsICertVerificationListener;
 
 /**
  * Extending nsIX509Cert
  */
-[scriptable, uuid(399004d8-b8c7-4eb9-8362-d99f4c0161fd)]
+[scriptable, uuid(09143cd9-dee3-4870-8450-8440c87e40e2)]
 interface nsIX509Cert3 : nsIX509Cert2 {
 
   /**
    *  Constants for specifying the chain mode when exporting a certificate
    */
   const unsigned long CMS_CHAIN_MODE_CertOnly = 1;
   const unsigned long CMS_CHAIN_MODE_CertChain = 2;
   const unsigned long CMS_CHAIN_MODE_CertChainWithRoot = 3;
@@ -83,16 +83,25 @@ interface nsIX509Cert3 : nsIX509Cert2 {
    * @param length On success, the number of entries in the returned array.
    * @return On success, an array containing the names of all tokens 
    *         the certificate is stored on (may be empty).
    *         On failure the function throws/returns an error.
    */
   void getAllTokenNames(out unsigned long length,
                        [retval, array, size_is(length)] out wstring
                        tokenNames);
+
+   /**
+   * Determine if the certificate can be verified for specific host name
+   *
+   * @param aHostName the hostname to be verified
+   * @return a boolean successful verification
+   */
+    bool isValidForHostname(in AUTF8String aHostName);
+
 };
 
 [scriptable, uuid(2fd0a785-9f2d-4327-8871-8c3e0783891d)]
 interface nsICertVerificationResult : nsISupports {
 
   /**
    *  This interface reflects a container of
    *  verification results. Call will not block.
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -934,16 +934,36 @@ nsNSSCertificate::GetAllTokenNames(PRUin
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNSSCertificate::IsValidForHostname(const nsACString & aHostName,
+                                     bool *retval)
+{
+  *retval = false;
+
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown())
+    return NS_ERROR_NOT_AVAILABLE;
+
+  if (!mCert)
+    return NS_OK;
+    
+  if (CERT_VerifyCertName(mCert,
+                          PromiseFlatCString(aHostName).get()) == SECSuccess)
+    *retval = true;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsNSSCertificate::GetSubjectName(nsAString &_subjectName)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   _subjectName.Truncate();
   if (mCert->subjectName) {