/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Red Hat, Inc. * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Kai Engert * Ehsan Akhgari * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCertOverrideService.h" #include "nsIX509Cert.h" #include "nsNSSCertificate.h" #include "nsCRT.h" #include "nsAppDirectoryServiceDefs.h" #include "nsStreamUtils.h" #include "nsNetUtil.h" #include "nsILineInputStream.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsISupportsPrimitives.h" #include "nsPromiseFlatString.h" #include "nsProxiedService.h" #include "nsStringBuffer.h" #include "nsAutoLock.h" #include "nsAutoPtr.h" #include "nspr.h" #include "pk11pub.h" #include "certdb.h" #include "sechash.h" #include "ssl.h" // For SSL_ClearSessionCache #include "nsNSSCleaner.h" NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate) static const char kCertOverrideFileName[] = "cert_override.txt"; void nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str) { str.Truncate(); if (ob & ob_Mismatch) str.Append('M'); if (ob & ob_Untrusted) str.Append('U'); if (ob & ob_Time_error) str.Append('T'); } void nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob) { const nsPromiseFlatCString &flat = PromiseFlatCString(str); const char *walk = flat.get(); ob = ob_None; for ( ; *walk; ++walk) { switch (*walk) { case 'm': case 'M': ob = (OverrideBits)(ob | ob_Mismatch); break; case 'u': case 'U': ob = (OverrideBits)(ob | ob_Untrusted); break; case 't': case 'T': ob = (OverrideBits)(ob | ob_Time_error); break; default: break; } } } NS_IMPL_THREADSAFE_ISUPPORTS3(nsCertOverrideService, nsICertOverrideService, nsIObserver, nsISupportsWeakReference) nsCertOverrideService::nsCertOverrideService() { monitor = PR_NewMonitor(); } nsCertOverrideService::~nsCertOverrideService() { if (monitor) PR_DestroyMonitor(monitor); } nsresult nsCertOverrideService::Init() { if (!mSettingsTable.Init()) return NS_ERROR_OUT_OF_MEMORY; mOidTagForStoringNewHashes = SEC_OID_SHA256; SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes); if (!od) return NS_ERROR_FAILURE; char *dotted_oid = CERT_GetOidString(&od->oid); if (!dotted_oid) return NS_ERROR_FAILURE; mDottedOidForStoringNewHashes = dotted_oid; PR_smprintf_free(dotted_oid); // cache mSettingsFile NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile)); if (mSettingsFile) { mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName)); } Read(); nsresult rv; NS_WITH_ALWAYS_PROXIED_SERVICE(nsIObserverService, mObserverService, "@mozilla.org/observer-service;1", NS_PROXY_TO_MAIN_THREAD, &rv); if (mObserverService) { mObserverService->AddObserver(this, "profile-before-change", PR_TRUE); mObserverService->AddObserver(this, "profile-do-change", PR_TRUE); mObserverService->AddObserver(this, "shutdown-cleanse", PR_TRUE); } return NS_OK; } NS_IMETHODIMP nsCertOverrideService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { // check the topic if (!nsCRT::strcmp(aTopic, "profile-before-change")) { // The profile is about to change, // or is going away because the application is shutting down. nsAutoMonitor lock(monitor); if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) { RemoveAllFromMemory(); // delete the storage file if (mSettingsFile) { mSettingsFile->Remove(PR_FALSE); } } else { RemoveAllFromMemory(); } } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { // The profile has already changed. // Now read from the new profile location. // we also need to update the cached file location nsAutoMonitor lock(monitor); nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile)); if (NS_SUCCEEDED(rv)) { mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName)); } Read(); } return NS_OK; } void nsCertOverrideService::RemoveAllFromMemory() { nsAutoMonitor lock(monitor); mSettingsTable.Clear(); } PR_STATIC_CALLBACK(PLDHashOperator) RemoveTemporariesCallback(nsCertOverrideEntry *aEntry, void *aArg) { if (aEntry && aEntry->mSettings.mIsTemporary) { aEntry->mSettings.mCert = nsnull; return PL_DHASH_REMOVE; } return PL_DHASH_NEXT; } void nsCertOverrideService::RemoveAllTemporaryOverrides() { { nsAutoMonitor lock(monitor); mSettingsTable.EnumerateEntries(RemoveTemporariesCallback, nsnull); // no need to write, as temporaries are never written to disk } } nsresult nsCertOverrideService::Read() { nsAutoMonitor lock(monitor); nsresult rv; nsCOMPtr fileInputStream; rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile); if (NS_FAILED(rv)) { return rv; } nsCOMPtr lineInputStream = do_QueryInterface(fileInputStream, &rv); if (NS_FAILED(rv)) { return rv; } nsCAutoString buffer; PRBool isMore = PR_TRUE; PRInt32 hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex; /* file format is: * * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey * * where override-mask is a sequence of characters, * M meaning hostname-Mismatch-override * U meaning Untrusted-override * T meaning Time-error-override (expired/not yet valid) * * if this format isn't respected we move onto the next line in the file. */ while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { if (buffer.IsEmpty() || buffer.First() == '#') { continue; } // this is a cheap, cheesy way of parsing a tab-delimited line into // string indexes, which can be lopped off into substrings. just for // purposes of obfuscation, it also checks that each token was found. // todo: use iterators? if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 || (fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 || (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 || (dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) { continue; } const nsASingleFragmentCString &tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1); const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1); const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1); const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1); const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex); nsCAutoString host(tmp); nsCertOverride::OverrideBits bits; nsCertOverride::convertStringToBits(bits_string, bits); PRInt32 port; PRInt32 portIndex = host.RFindChar(':'); if (portIndex == kNotFound) continue; // Ignore broken entries PRInt32 portParseError; nsCAutoString portString(Substring(host, portIndex+1)); port = portString.ToInteger(&portParseError); if (portParseError) continue; // Ignore broken entries host.Truncate(portIndex); AddEntryToList(host, port, nsnull, // don't have the cert PR_FALSE, // not temporary algo_string, fingerprint, bits, db_key); } return NS_OK; } PR_STATIC_CALLBACK(PLDHashOperator) WriteEntryCallback(nsCertOverrideEntry *aEntry, void *aArg) { static const char kTab[] = "\t"; nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg; nsresult rv; if (rawStreamPtr && aEntry) { const nsCertOverride &settings = aEntry->mSettings; if (settings.mIsTemporary) return PL_DHASH_NEXT; nsCAutoString bits_string; nsCertOverride::convertBitsToString(settings.mOverrideBits, bits_string); rawStreamPtr->Write(aEntry->mHostWithPort.get(), aEntry->mHostWithPort.Length(), &rv); rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv); rawStreamPtr->Write(settings.mFingerprintAlgOID.get(), settings.mFingerprintAlgOID.Length(), &rv); rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv); rawStreamPtr->Write(settings.mFingerprint.get(), settings.mFingerprint.Length(), &rv); rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv); rawStreamPtr->Write(bits_string.get(), bits_string.Length(), &rv); rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv); rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &rv); rawStreamPtr->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &rv); } return PL_DHASH_NEXT; } nsresult nsCertOverrideService::Write() { nsAutoMonitor lock(monitor); if (!mSettingsFile) { return NS_ERROR_NULL_POINTER; } nsresult rv; nsCOMPtr fileOutputStream; rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), mSettingsFile, -1, 0600); if (NS_FAILED(rv)) { NS_ERROR("failed to open cert_warn_settings.txt for writing"); return rv; } // get a buffered output stream 4096 bytes big, to optimize writes nsCOMPtr bufferedOutputStream; rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096); if (NS_FAILED(rv)) { return rv; } static const char kHeader[] = "# PSM Certificate Override Settings file" NS_LINEBREAK "# This is a generated file! Do not edit." NS_LINEBREAK; /* see ::Read for file format */ bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv); nsIOutputStream *rawStreamPtr = bufferedOutputStream; mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr); // All went ok. Maybe except for problems in Write(), but the stream detects // that for us nsCOMPtr safeStream = do_QueryInterface(bufferedOutputStream); NS_ASSERTION(safeStream, "expected a safe output stream!"); if (safeStream) { rv = safeStream->Finish(); if (NS_FAILED(rv)) { NS_WARNING("failed to save cert warn settings file! possible dataloss"); return rv; } } return NS_OK; } static nsresult GetCertFingerprintByOidTag(CERTCertificate* nsscert, SECOidTag aOidTag, nsCString &fp) { unsigned int hash_len = HASH_ResultLenByOidTag(aOidTag); nsStringBuffer* fingerprint = nsStringBuffer::Alloc(hash_len); if (!fingerprint) return NS_ERROR_OUT_OF_MEMORY; PK11_HashBuf(aOidTag, (unsigned char*)fingerprint->Data(), nsscert->derCert.data, nsscert->derCert.len); SECItem fpItem; fpItem.data = (unsigned char*)fingerprint->Data(); fpItem.len = hash_len; char *tmpstr = CERT_Hexify(&fpItem, 1); fp.Assign(tmpstr); PORT_Free(tmpstr); fingerprint->Release(); return NS_OK; } static nsresult GetCertFingerprintByOidTag(nsIX509Cert *aCert, SECOidTag aOidTag, nsCString &fp) { nsCOMPtr cert2 = do_QueryInterface(aCert); if (!cert2) return NS_ERROR_FAILURE; CERTCertificate* nsscert = cert2->GetCert(); if (!nsscert) return NS_ERROR_FAILURE; CERTCertificateCleaner nsscertCleaner(nsscert); return GetCertFingerprintByOidTag(nsscert, aOidTag, fp); } static nsresult GetCertFingerprintByDottedOidString(CERTCertificate* nsscert, const nsCString &dottedOid, nsCString &fp) { SECItem oid; oid.data = nsnull; oid.len = 0; SECStatus srv = SEC_StringToOID(nsnull, &oid, dottedOid.get(), dottedOid.Length()); if (srv != SECSuccess) return NS_ERROR_FAILURE; SECOidTag oid_tag = SECOID_FindOIDTag(&oid); SECITEM_FreeItem(&oid, PR_FALSE); if (oid_tag == SEC_OID_UNKNOWN) return NS_ERROR_FAILURE; return GetCertFingerprintByOidTag(nsscert, oid_tag, fp); } static nsresult GetCertFingerprintByDottedOidString(nsIX509Cert *aCert, const nsCString &dottedOid, nsCString &fp) { nsCOMPtr cert2 = do_QueryInterface(aCert); if (!cert2) return NS_ERROR_FAILURE; CERTCertificate* nsscert = cert2->GetCert(); if (!nsscert) return NS_ERROR_FAILURE; CERTCertificateCleaner nsscertCleaner(nsscert); return GetCertFingerprintByDottedOidString(nsscert, dottedOid, fp); } NS_IMETHODIMP nsCertOverrideService::RememberValidityOverride(const nsACString & aHostName, PRInt32 aPort, nsIX509Cert *aCert, PRUint32 aOverrideBits, PRBool aTemporary) { NS_ENSURE_ARG_POINTER(aCert); if (aHostName.IsEmpty()) return NS_ERROR_INVALID_ARG; if (aPort < -1) return NS_ERROR_INVALID_ARG; nsCOMPtr cert2 = do_QueryInterface(aCert); if (!cert2) return NS_ERROR_FAILURE; CERTCertificate* nsscert = cert2->GetCert(); if (!nsscert) return NS_ERROR_FAILURE; CERTCertificateCleaner nsscertCleaner(nsscert); nsCAutoString nickname; nickname = nsNSSCertificate::defaultServerNickname(nsscert); if (!aTemporary && !nickname.IsEmpty()) { PK11SlotInfo *slot = PK11_GetInternalKeySlot(); if (!slot) return NS_ERROR_FAILURE; SECStatus srv = PK11_ImportCert(slot, nsscert, CK_INVALID_HANDLE, const_cast(nickname.get()), PR_FALSE); PK11_FreeSlot(slot); if (srv != SECSuccess) return NS_ERROR_FAILURE; } nsCAutoString fpStr; nsresult rv = GetCertFingerprintByOidTag(nsscert, mOidTagForStoringNewHashes, fpStr); if (NS_FAILED(rv)) return rv; char *dbkey = NULL; rv = aCert->GetDbKey(&dbkey); if (NS_FAILED(rv) || !dbkey) return rv; // change \n and \r to spaces in the possibly multi-line-base64-encoded key for (char *dbkey_walk = dbkey; *dbkey_walk; ++dbkey_walk) { char c = *dbkey_walk; if (c == '\r' || c == '\n') { *dbkey_walk = ' '; } } { nsAutoMonitor lock(monitor); AddEntryToList(aHostName, aPort, aTemporary ? aCert : nsnull, // keep a reference to the cert for temporary overrides aTemporary, mDottedOidForStoringNewHashes, fpStr, (nsCertOverride::OverrideBits)aOverrideBits, nsDependentCString(dbkey)); Write(); } PR_Free(dbkey); return NS_OK; } NS_IMETHODIMP nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, PRInt32 aPort, nsIX509Cert *aCert, PRUint32 *aOverrideBits, PRBool *aIsTemporary, PRBool *_retval) { if (aHostName.IsEmpty()) return NS_ERROR_INVALID_ARG; if (aPort < -1) return NS_ERROR_INVALID_ARG; NS_ENSURE_ARG_POINTER(aCert); NS_ENSURE_ARG_POINTER(aOverrideBits); NS_ENSURE_ARG_POINTER(aIsTemporary); NS_ENSURE_ARG_POINTER(_retval); *_retval = PR_FALSE; *aOverrideBits = nsCertOverride::ob_None; nsCAutoString hostPort; GetHostWithPort(aHostName, aPort, hostPort); nsCertOverride settings; { nsAutoMonitor lock(monitor); nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); if (!entry) return NS_OK; settings = entry->mSettings; // copy } *aOverrideBits = settings.mOverrideBits; *aIsTemporary = settings.mIsTemporary; nsCAutoString fpStr; nsresult rv; if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) { rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr); } else { rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr); } if (NS_FAILED(rv)) return rv; *_retval = settings.mFingerprint.Equals(fpStr); return NS_OK; } NS_IMETHODIMP nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, PRInt32 aPort, nsACString & aHashAlg, nsACString & aFingerprint, PRUint32 *aOverrideBits, PRBool *aIsTemporary, PRBool *_found) { NS_ENSURE_ARG_POINTER(_found); NS_ENSURE_ARG_POINTER(aIsTemporary); NS_ENSURE_ARG_POINTER(aOverrideBits); *_found = PR_FALSE; *aOverrideBits = nsCertOverride::ob_None; nsCAutoString hostPort; GetHostWithPort(aHostName, aPort, hostPort); nsCertOverride settings; { nsAutoMonitor lock(monitor); nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); if (entry) { *_found = PR_TRUE; settings = entry->mSettings; // copy } } if (*_found) { *aOverrideBits = settings.mOverrideBits; *aIsTemporary = settings.mIsTemporary; aFingerprint = settings.mFingerprint; aHashAlg = settings.mFingerprintAlgOID; } return NS_OK; } nsresult nsCertOverrideService::AddEntryToList(const nsACString &aHostName, PRInt32 aPort, nsIX509Cert *aCert, const PRBool aIsTemporary, const nsACString &fingerprintAlgOID, const nsACString &fingerprint, nsCertOverride::OverrideBits ob, const nsACString &dbKey) { nsCAutoString hostPort; GetHostWithPort(aHostName, aPort, hostPort); { nsAutoMonitor lock(monitor); nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get()); if (!entry) { NS_ERROR("can't insert a null entry!"); return NS_ERROR_OUT_OF_MEMORY; } entry->mHostWithPort = hostPort; nsCertOverride &settings = entry->mSettings; settings.mAsciiHost = aHostName; settings.mPort = aPort; settings.mIsTemporary = aIsTemporary; settings.mFingerprintAlgOID = fingerprintAlgOID; settings.mFingerprint = fingerprint; settings.mOverrideBits = ob; settings.mDBKey = dbKey; settings.mCert = aCert; } return NS_OK; } NS_IMETHODIMP nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, PRInt32 aPort) { nsCAutoString hostPort; GetHostWithPort(aHostName, aPort, hostPort); { nsAutoMonitor lock(monitor); mSettingsTable.RemoveEntry(hostPort.get()); Write(); } SSL_ClearSessionCache(); return NS_OK; } NS_IMETHODIMP nsCertOverrideService::GetAllOverrideHostsWithPorts(PRUint32 *aCount, PRUnichar ***aHostsWithPortsArray) { return NS_ERROR_NOT_IMPLEMENTED; } static PRBool matchesDBKey(nsIX509Cert *cert, const char *match_dbkey) { char *dbkey = NULL; nsresult rv = cert->GetDbKey(&dbkey); if (NS_FAILED(rv) || !dbkey) return PR_FALSE; PRBool found_mismatch = PR_FALSE; const char *key1 = dbkey; const char *key2 = match_dbkey; // skip over any whitespace when comparing while (*key1 && *key2) { char c1 = *key1; char c2 = *key2; switch (c1) { case ' ': case '\t': case '\n': case '\r': ++key1; continue; } switch (c2) { case ' ': case '\t': case '\n': case '\r': ++key2; continue; } if (c1 != c2) { found_mismatch = PR_TRUE; break; } ++key1; ++key2; } PR_Free(dbkey); return !found_mismatch; } struct nsCertAndBoolsAndInt { nsIX509Cert *cert; PRBool aCheckTemporaries; PRBool aCheckPermanents; PRUint32 counter; SECOidTag mOidTagForStoringNewHashes; nsCString mDottedOidForStoringNewHashes; }; PR_STATIC_CALLBACK(PLDHashOperator) FindMatchingCertCallback(nsCertOverrideEntry *aEntry, void *aArg) { nsCertAndBoolsAndInt *cai = (nsCertAndBoolsAndInt *)aArg; if (cai && aEntry) { const nsCertOverride &settings = aEntry->mSettings; PRBool still_ok = PR_TRUE; if ((settings.mIsTemporary && !cai->aCheckTemporaries) || (!settings.mIsTemporary && !cai->aCheckPermanents)) { still_ok = PR_FALSE; } if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) { nsCAutoString cert_fingerprint; nsresult rv; if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) { rv = GetCertFingerprintByOidTag(cai->cert, cai->mOidTagForStoringNewHashes, cert_fingerprint); } else { rv = GetCertFingerprintByDottedOidString(cai->cert, settings.mFingerprintAlgOID, cert_fingerprint); } if (NS_SUCCEEDED(rv) && settings.mFingerprint.Equals(cert_fingerprint)) { cai->counter++; } } } return PL_DHASH_NEXT; } NS_IMETHODIMP nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert, PRBool aCheckTemporaries, PRBool aCheckPermanents, PRUint32 *_retval) { NS_ENSURE_ARG(aCert); NS_ENSURE_ARG(_retval); nsCertAndBoolsAndInt cai; cai.cert = aCert; cai.aCheckTemporaries = aCheckTemporaries; cai.aCheckPermanents = aCheckPermanents; cai.counter = 0; cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes; cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes; { nsAutoMonitor lock(monitor); mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai); } *_retval = cai.counter; return NS_OK; } struct nsCertAndPointerAndCallback { nsIX509Cert *cert; void *userdata; nsCertOverrideService::CertOverrideEnumerator enumerator; SECOidTag mOidTagForStoringNewHashes; nsCString mDottedOidForStoringNewHashes; }; PR_STATIC_CALLBACK(PLDHashOperator) EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry, void *aArg) { nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg; if (capac && aEntry) { const nsCertOverride &settings = aEntry->mSettings; if (!capac->cert) { (*capac->enumerator)(settings, capac->userdata); } else { if (matchesDBKey(capac->cert, settings.mDBKey.get())) { nsCAutoString cert_fingerprint; nsresult rv; if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) { rv = GetCertFingerprintByOidTag(capac->cert, capac->mOidTagForStoringNewHashes, cert_fingerprint); } else { rv = GetCertFingerprintByDottedOidString(capac->cert, settings.mFingerprintAlgOID, cert_fingerprint); } if (NS_SUCCEEDED(rv) && settings.mFingerprint.Equals(cert_fingerprint)) { (*capac->enumerator)(settings, capac->userdata); } } } } return PL_DHASH_NEXT; } nsresult nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert, CertOverrideEnumerator enumerator, void *aUserData) { nsCertAndPointerAndCallback capac; capac.cert = aCert; capac.userdata = aUserData; capac.enumerator = enumerator; capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes; capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes; { nsAutoMonitor lock(monitor); mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac); } return NS_OK; } void nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, PRInt32 aPort, nsACString& _retval) { nsCAutoString hostPort(aHostName); if (aPort == -1) { aPort = 443; } if (!hostPort.IsEmpty()) { hostPort.AppendLiteral(":"); hostPort.AppendInt(aPort); } _retval.Assign(hostPort); }