diff --git a/mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp b/mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp index 8770bd98e1f..dd5f4954576 100644 --- a/mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp +++ b/mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp @@ -265,15 +265,52 @@ SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd, // first the default action SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); + // We want to remember the CA certs in the temp db, so that the application can find the + // complete chain at any time it might need it. + // But we keep only those CA certs in the temp db, that we didn't already know. + if (SECSuccess == rv) { - nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; - if (infoObject) { - CERTCertificate *serverCert = SSL_PeerCertificate(fd); - if (serverCert) { - CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA); - infoObject->RememberCAChain(certList); - CERT_DestroyCertificate(serverCert); + CERTCertificate *serverCert = SSL_PeerCertificate(fd); + if (serverCert) { + CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA); + + nsCOMPtr nssComponent; + + for (CERTCertListNode *node = CERT_LIST_HEAD(certList); + !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + + if (node->cert->slot) { + // This cert was found on a token, no need to remember it in the temp db. + continue; + } + + if (node->cert->isperm) { + // We don't need to remember certs already stored in perm db. + continue; + } + + if (node->cert == serverCert) { + // We don't want to remember the server cert, + // the code that cares for displaying page info does this already. + continue; + } + + // We have found a signer cert that we want to remember. + + if (!nssComponent) { + // delay getting the service until we really need it + nsresult rv; + nssComponent = do_GetService(kNSSComponentCID, &rv); + } + + if (nssComponent) { + nssComponent->RememberCert(node->cert); + } } + + CERT_DestroyCertList(certList); + CERT_DestroyCertificate(serverCert); } } diff --git a/mozilla/security/manager/ssl/src/nsNSSComponent.cpp b/mozilla/security/manager/ssl/src/nsNSSComponent.cpp index 543f4ec89e6..54e7d685e2d 100644 --- a/mozilla/security/manager/ssl/src/nsNSSComponent.cpp +++ b/mozilla/security/manager/ssl/src/nsNSSComponent.cpp @@ -100,9 +100,81 @@ extern char * pk11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void *arg); #define PIPNSS_STRBUNDLE_URL "chrome://pipnss/locale/pipnss.properties" +static PLHashNumber PR_CALLBACK certHashtable_keyHash(const void *key) +{ + if (!key) + return 0; + + SECItem *certKey = (SECItem*)key; + + // lazy hash function, sum up all char values of SECItem + + PLHashNumber hash = 0; + unsigned int i = 0; + unsigned char *c = certKey->data; + + for (i = 0; i < certKey->len; ++i, ++c) { + hash += *c; + } + + return hash; +} + +static PRIntn PR_CALLBACK certHashtable_keyCompare(const void *k1, const void *k2) +{ + // return type is a bool, answering the question "are the keys equal?" + + if (!k1 || !k2) + return PR_FALSE; + + SECItem *certKey1 = (SECItem*)k1; + SECItem *certKey2 = (SECItem*)k2; + + if (certKey1->len != certKey2->len) { + return PR_FALSE; + } + + unsigned int i = 0; + unsigned char *c1 = certKey1->data; + unsigned char *c2 = certKey2->data; + + for (i = 0; i < certKey1->len; ++i, ++c1, ++c2) { + if (*c1 != *c2) { + return PR_FALSE; + } + } + + return PR_TRUE; +} + +static PRIntn PR_CALLBACK certHashtable_valueCompare(const void *v1, const void *v2) +{ + // two values are identical if their keys are identical + + if (!v1 || !v2) + return PR_FALSE; + + CERTCertificate *cert1 = (CERTCertificate*)v1; + CERTCertificate *cert2 = (CERTCertificate*)v2; + + return certHashtable_keyCompare(&cert1->certKey, &cert2->certKey); +} + +static PRIntn PR_CALLBACK certHashtable_clearEntry(PLHashEntry *he, PRIntn /*index*/, void * /*userdata*/) +{ + if (he && he->value) { + CERT_DestroyCertificate((CERTCertificate*)he->value); + } + + return HT_ENUMERATE_NEXT; +} + nsNSSComponent::nsNSSComponent() { NS_INIT_ISUPPORTS(); + + hashTableCerts = PL_NewHashTable( 0, certHashtable_keyHash, certHashtable_keyCompare, + certHashtable_valueCompare, 0, 0 ); } nsNSSComponent::~nsNSSComponent() @@ -119,6 +191,12 @@ nsNSSComponent::~nsNSSComponent() mPref->UnregisterCallback("security.", nsNSSComponent::PrefChangedCallback, (void*) this); + if (hashTableCerts) { + PL_HashTableEnumerateEntries(hashTableCerts, certHashtable_clearEntry, 0); + PL_HashTableDestroy(hashTableCerts); + hashTableCerts = 0; + } + if (mNSSInitialized) NSS_Shutdown(); @@ -806,6 +884,35 @@ nsNSSComponent::RegisterProfileChangeObserver() return NS_OK; } +NS_IMETHODIMP +nsNSSComponent::RememberCert(CERTCertificate *cert) +{ + if (!hashTableCerts || !cert) + return NS_OK; + + void *found = PL_HashTableLookup(hashTableCerts, (void*)&cert->certKey); + + if (found) { + // we remember that cert already + return NS_OK; + } + + CERTCertificate *myDupCert = CERT_DupCertificate(cert); + + if (!myDupCert) + return NS_ERROR_OUT_OF_MEMORY; + + if (!PL_HashTableAdd(hashTableCerts, (void*)&myDupCert->certKey, myDupCert)) { + CERT_DestroyCertificate(myDupCert); + } + + return NS_OK; +} + + + + + NS_IMPL_ISUPPORTS1(PipUIContext, nsIInterfaceRequestor) PipUIContext::PipUIContext() diff --git a/mozilla/security/manager/ssl/src/nsNSSComponent.h b/mozilla/security/manager/ssl/src/nsNSSComponent.h index d32b7c75eec..9e64e5166a4 100644 --- a/mozilla/security/manager/ssl/src/nsNSSComponent.h +++ b/mozilla/security/manager/ssl/src/nsNSSComponent.h @@ -76,6 +76,7 @@ class NS_NO_VTABLE nsINSSComponent : public nsISupports { // values in the preferences. NS_IMETHOD EnableOCSP() = 0; + NS_IMETHOD RememberCert(CERTCertificate *cert) = 0; }; @@ -111,6 +112,7 @@ public: NS_IMETHOD DisableOCSP(); NS_IMETHOD EnableOCSP(); nsresult InitializeNSS(); + NS_IMETHOD RememberCert(CERTCertificate *cert); private: @@ -128,6 +130,7 @@ private: nsCOMPtr mPSMContentListener; nsCOMPtr mPref; static PRBool mNSSInitialized; + PLHashTable *hashTableCerts; }; //-------------------------------------------- diff --git a/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp b/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp index 3e7d549b4e7..a26268c0396 100644 --- a/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/mozilla/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -155,17 +155,13 @@ nsNSSSocketInfo::nsNSSSocketInfo() mForTLSStepUp(PR_FALSE), mFirstWrite(PR_TRUE), mTLSIntolerant(PR_FALSE), - mPort(0), - mCAChain(nsnull) + mPort(0) { NS_INIT_ISUPPORTS(); } nsNSSSocketInfo::~nsNSSSocketInfo() { - if (mCAChain) { - CERT_DestroyCertList(mCAChain); - } } NS_IMPL_THREADSAFE_ISUPPORTS4(nsNSSSocketInfo, @@ -368,15 +364,6 @@ nsresult nsNSSSocketInfo::GetSSLStatus(nsISSLStatus** _result) return NS_OK; } -nsresult nsNSSSocketInfo::RememberCAChain(CERTCertList *aCertList) -{ - if (mCAChain) { - CERT_DestroyCertList(mCAChain); - } - mCAChain = aCertList; - return NS_OK; -} - nsresult nsNSSSocketInfo::SetSSLStatus(nsISSLStatus *aSSLStatus) { mSSLStatus = aSSLStatus; diff --git a/mozilla/security/manager/ssl/src/nsNSSIOLayer.h b/mozilla/security/manager/ssl/src/nsNSSIOLayer.h index e0561ef509b..e72a3087292 100644 --- a/mozilla/security/manager/ssl/src/nsNSSIOLayer.h +++ b/mozilla/security/manager/ssl/src/nsNSSIOLayer.h @@ -73,8 +73,6 @@ public: nsresult GetTLSIntolerant(PRBool *aTLSIntolerant); nsresult SetTLSIntolerant(PRBool aTLSIntolerant); - nsresult RememberCAChain(CERTCertList *aCertList); - /* Set SSL Status values */ nsresult SetSSLStatus(nsISSLStatus *aSSLStatus); @@ -89,7 +87,6 @@ protected: PRBool mTLSIntolerant; PRInt32 mPort; nsXPIDLCString mHostName; - CERTCertList *mCAChain; /* SSL Status */ nsCOMPtr mSSLStatus;