michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCertOverrideService.h" michael@0: michael@0: #include "pkix/pkixtypes.h" michael@0: #include "nsIX509Cert.h" michael@0: #include "NSSCertDBTrustDomain.h" michael@0: #include "nsNSSCertificate.h" michael@0: #include "nsNSSCertHelper.h" michael@0: #include "nsCRT.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsILineInputStream.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsPromiseFlatString.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsStringBuffer.h" michael@0: #include "ScopedNSSTypes.h" michael@0: #include "SharedSSLState.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "pk11pub.h" michael@0: #include "certdb.h" michael@0: #include "sechash.h" michael@0: #include "ssl.h" // For SSL_ClearSessionCache michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::psm; michael@0: michael@0: static const char kCertOverrideFileName[] = "cert_override.txt"; michael@0: michael@0: void michael@0: nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str) michael@0: { michael@0: str.Truncate(); michael@0: michael@0: if (ob & ob_Mismatch) michael@0: str.Append('M'); michael@0: michael@0: if (ob & ob_Untrusted) michael@0: str.Append('U'); michael@0: michael@0: if (ob & ob_Time_error) michael@0: str.Append('T'); michael@0: } michael@0: michael@0: void michael@0: nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob) michael@0: { michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(str); michael@0: const char *walk = flat.get(); michael@0: michael@0: ob = ob_None; michael@0: michael@0: for ( ; *walk; ++walk) michael@0: { michael@0: switch (*walk) michael@0: { michael@0: case 'm': michael@0: case 'M': michael@0: ob = (OverrideBits)(ob | ob_Mismatch); michael@0: break; michael@0: michael@0: case 'u': michael@0: case 'U': michael@0: ob = (OverrideBits)(ob | ob_Untrusted); michael@0: break; michael@0: michael@0: case 't': michael@0: case 'T': michael@0: ob = (OverrideBits)(ob | ob_Time_error); michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCertOverrideService, michael@0: nsICertOverrideService, michael@0: nsIObserver, michael@0: nsISupportsWeakReference) michael@0: michael@0: nsCertOverrideService::nsCertOverrideService() michael@0: : monitor("nsCertOverrideService.monitor") michael@0: { michael@0: } michael@0: michael@0: nsCertOverrideService::~nsCertOverrideService() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsCertOverrideService::Init() michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_NOTREACHED("nsCertOverrideService initialized off main thread"); michael@0: return NS_ERROR_NOT_SAME_THREAD; michael@0: } michael@0: michael@0: mOidTagForStoringNewHashes = SEC_OID_SHA256; michael@0: michael@0: SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes); michael@0: if (!od) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: char *dotted_oid = CERT_GetOidString(&od->oid); michael@0: if (!dotted_oid) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mDottedOidForStoringNewHashes = dotted_oid; michael@0: PR_smprintf_free(dotted_oid); michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: michael@0: // If we cannot add ourselves as a profile change observer, then we will not michael@0: // attempt to read/write any settings file. Otherwise, we would end up michael@0: // reading/writing the wrong settings file after a profile change. michael@0: if (observerService) { michael@0: observerService->AddObserver(this, "profile-before-change", true); michael@0: observerService->AddObserver(this, "profile-do-change", true); michael@0: // simulate a profile change so we read the current profile's settings file michael@0: Observe(nullptr, "profile-do-change", nullptr); michael@0: } michael@0: michael@0: SharedSSLState::NoteCertOverrideServiceInstantiated(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::Observe(nsISupports *, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: // check the topic michael@0: if (!nsCRT::strcmp(aTopic, "profile-before-change")) { michael@0: // The profile is about to change, michael@0: // or is going away because the application is shutting down. michael@0: michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: michael@0: if (!nsCRT::strcmp(aData, MOZ_UTF16("shutdown-cleanse"))) { michael@0: RemoveAllFromMemory(); michael@0: // delete the storage file michael@0: if (mSettingsFile) { michael@0: mSettingsFile->Remove(false); michael@0: } michael@0: } else { michael@0: RemoveAllFromMemory(); michael@0: } michael@0: michael@0: } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { michael@0: // The profile has already changed. michael@0: // Now read from the new profile location. michael@0: // we also need to update the cached file location michael@0: michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: michael@0: nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName)); michael@0: } else { michael@0: mSettingsFile = nullptr; michael@0: } michael@0: Read(); michael@0: michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsCertOverrideService::RemoveAllFromMemory() michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: mSettingsTable.Clear(); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: RemoveTemporariesCallback(nsCertOverrideEntry *aEntry, michael@0: void *aArg) michael@0: { michael@0: if (aEntry && aEntry->mSettings.mIsTemporary) { michael@0: aEntry->mSettings.mCert = nullptr; michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: nsCertOverrideService::RemoveAllTemporaryOverrides() michael@0: { michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: mSettingsTable.EnumerateEntries(RemoveTemporariesCallback, nullptr); michael@0: // no need to write, as temporaries are never written to disk michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsCertOverrideService::Read() michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: michael@0: // If we don't have a profile, then we won't try to read any settings file. michael@0: if (!mSettingsFile) michael@0: return NS_OK; michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr fileInputStream; michael@0: rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr lineInputStream = do_QueryInterface(fileInputStream, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString buffer; michael@0: bool isMore = true; michael@0: int32_t hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex; michael@0: michael@0: /* file format is: michael@0: * michael@0: * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey michael@0: * michael@0: * where override-mask is a sequence of characters, michael@0: * M meaning hostname-Mismatch-override michael@0: * U meaning Untrusted-override michael@0: * T meaning Time-error-override (expired/not yet valid) michael@0: * michael@0: * if this format isn't respected we move onto the next line in the file. michael@0: */ michael@0: michael@0: while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { michael@0: if (buffer.IsEmpty() || buffer.First() == '#') { michael@0: continue; michael@0: } michael@0: michael@0: // this is a cheap, cheesy way of parsing a tab-delimited line into michael@0: // string indexes, which can be lopped off into substrings. just for michael@0: // purposes of obfuscation, it also checks that each token was found. michael@0: // todo: use iterators? michael@0: if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 || michael@0: (fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 || michael@0: (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 || michael@0: (dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) { michael@0: continue; michael@0: } michael@0: michael@0: const nsASingleFragmentCString &tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1); michael@0: const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1); michael@0: const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1); michael@0: const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1); michael@0: const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex); michael@0: michael@0: nsAutoCString host(tmp); michael@0: nsCertOverride::OverrideBits bits; michael@0: nsCertOverride::convertStringToBits(bits_string, bits); michael@0: michael@0: int32_t port; michael@0: int32_t portIndex = host.RFindChar(':'); michael@0: if (portIndex == kNotFound) michael@0: continue; // Ignore broken entries michael@0: michael@0: nsresult portParseError; michael@0: nsAutoCString portString(Substring(host, portIndex+1)); michael@0: port = portString.ToInteger(&portParseError); michael@0: if (NS_FAILED(portParseError)) michael@0: continue; // Ignore broken entries michael@0: michael@0: host.Truncate(portIndex); michael@0: michael@0: AddEntryToList(host, port, michael@0: nullptr, // don't have the cert michael@0: false, // not temporary michael@0: algo_string, fingerprint, bits, db_key); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: WriteEntryCallback(nsCertOverrideEntry *aEntry, michael@0: void *aArg) michael@0: { michael@0: static const char kTab[] = "\t"; michael@0: michael@0: nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg; michael@0: michael@0: uint32_t unused; michael@0: michael@0: if (rawStreamPtr && aEntry) michael@0: { michael@0: const nsCertOverride &settings = aEntry->mSettings; michael@0: if (settings.mIsTemporary) michael@0: return PL_DHASH_NEXT; michael@0: michael@0: nsAutoCString bits_string; michael@0: nsCertOverride::convertBitsToString(settings.mOverrideBits, michael@0: bits_string); michael@0: michael@0: rawStreamPtr->Write(aEntry->mHostWithPort.get(), aEntry->mHostWithPort.Length(), &unused); michael@0: rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); michael@0: rawStreamPtr->Write(settings.mFingerprintAlgOID.get(), michael@0: settings.mFingerprintAlgOID.Length(), &unused); michael@0: rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); michael@0: rawStreamPtr->Write(settings.mFingerprint.get(), michael@0: settings.mFingerprint.Length(), &unused); michael@0: rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); michael@0: rawStreamPtr->Write(bits_string.get(), michael@0: bits_string.Length(), &unused); michael@0: rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused); michael@0: rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &unused); michael@0: rawStreamPtr->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsresult michael@0: nsCertOverrideService::Write() michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: michael@0: // If we don't have any profile, then we won't try to write any file michael@0: if (!mSettingsFile) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr fileOutputStream; michael@0: rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), michael@0: mSettingsFile, michael@0: -1, michael@0: 0600); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("failed to open cert_warn_settings.txt for writing"); michael@0: return rv; michael@0: } michael@0: michael@0: // get a buffered output stream 4096 bytes big, to optimize writes michael@0: nsCOMPtr bufferedOutputStream; michael@0: rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: static const char kHeader[] = michael@0: "# PSM Certificate Override Settings file" NS_LINEBREAK michael@0: "# This is a generated file! Do not edit." NS_LINEBREAK; michael@0: michael@0: /* see ::Read for file format */ michael@0: michael@0: uint32_t unused; michael@0: bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &unused); michael@0: michael@0: nsIOutputStream *rawStreamPtr = bufferedOutputStream; michael@0: mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr); michael@0: michael@0: // All went ok. Maybe except for problems in Write(), but the stream detects michael@0: // that for us michael@0: nsCOMPtr safeStream = do_QueryInterface(bufferedOutputStream); michael@0: NS_ASSERTION(safeStream, "expected a safe output stream!"); michael@0: if (safeStream) { michael@0: rv = safeStream->Finish(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("failed to save cert warn settings file! possible dataloss"); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: GetCertFingerprintByOidTag(nsIX509Cert *aCert, michael@0: SECOidTag aOidTag, michael@0: nsCString &fp) michael@0: { michael@0: nsCOMPtr cert2 = do_QueryInterface(aCert); michael@0: if (!cert2) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mozilla::pkix::ScopedCERTCertificate nsscert(cert2->GetCert()); michael@0: if (!nsscert) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return GetCertFingerprintByOidTag(nsscert.get(), aOidTag, fp); michael@0: } michael@0: michael@0: static nsresult michael@0: GetCertFingerprintByDottedOidString(CERTCertificate* nsscert, michael@0: const nsCString &dottedOid, michael@0: nsCString &fp) michael@0: { michael@0: SECItem oid; michael@0: oid.data = nullptr; michael@0: oid.len = 0; michael@0: SECStatus srv = SEC_StringToOID(nullptr, &oid, michael@0: dottedOid.get(), dottedOid.Length()); michael@0: if (srv != SECSuccess) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: SECOidTag oid_tag = SECOID_FindOIDTag(&oid); michael@0: SECITEM_FreeItem(&oid, false); michael@0: michael@0: if (oid_tag == SEC_OID_UNKNOWN) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return GetCertFingerprintByOidTag(nsscert, oid_tag, fp); michael@0: } michael@0: michael@0: static nsresult michael@0: GetCertFingerprintByDottedOidString(nsIX509Cert *aCert, michael@0: const nsCString &dottedOid, michael@0: nsCString &fp) michael@0: { michael@0: nsCOMPtr cert2 = do_QueryInterface(aCert); michael@0: if (!cert2) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mozilla::pkix::ScopedCERTCertificate nsscert(cert2->GetCert()); michael@0: if (!nsscert) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return GetCertFingerprintByDottedOidString(nsscert.get(), dottedOid, fp); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::RememberValidityOverride(const nsACString & aHostName, int32_t aPort, michael@0: nsIX509Cert *aCert, michael@0: uint32_t aOverrideBits, michael@0: bool aTemporary) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCert); michael@0: if (aHostName.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (aPort < -1) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr cert2 = do_QueryInterface(aCert); michael@0: if (!cert2) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mozilla::pkix::ScopedCERTCertificate nsscert(cert2->GetCert()); michael@0: if (!nsscert) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: char* nickname = DefaultServerNicknameForCert(nsscert.get()); michael@0: if (!aTemporary && nickname && *nickname) michael@0: { michael@0: ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); michael@0: if (!slot) { michael@0: PR_Free(nickname); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: SECStatus srv = PK11_ImportCert(slot, nsscert.get(), CK_INVALID_HANDLE, michael@0: nickname, false); michael@0: if (srv != SECSuccess) { michael@0: PR_Free(nickname); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: PR_FREEIF(nickname); michael@0: michael@0: nsAutoCString fpStr; michael@0: nsresult rv = GetCertFingerprintByOidTag(nsscert.get(), michael@0: mOidTagForStoringNewHashes, fpStr); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: char *dbkey = nullptr; michael@0: rv = aCert->GetDbKey(&dbkey); michael@0: if (NS_FAILED(rv) || !dbkey) michael@0: return rv; michael@0: michael@0: // change \n and \r to spaces in the possibly multi-line-base64-encoded key michael@0: for (char *dbkey_walk = dbkey; michael@0: *dbkey_walk; michael@0: ++dbkey_walk) { michael@0: char c = *dbkey_walk; michael@0: if (c == '\r' || c == '\n') { michael@0: *dbkey_walk = ' '; michael@0: } michael@0: } michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: AddEntryToList(aHostName, aPort, michael@0: aTemporary ? aCert : nullptr, michael@0: // keep a reference to the cert for temporary overrides michael@0: aTemporary, michael@0: mDottedOidForStoringNewHashes, fpStr, michael@0: (nsCertOverride::OverrideBits)aOverrideBits, michael@0: nsDependentCString(dbkey)); michael@0: Write(); michael@0: } michael@0: michael@0: PR_Free(dbkey); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, int32_t aPort, michael@0: nsIX509Cert *aCert, michael@0: uint32_t *aOverrideBits, michael@0: bool *aIsTemporary, michael@0: bool *_retval) michael@0: { michael@0: if (aHostName.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (aPort < -1) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aCert); michael@0: NS_ENSURE_ARG_POINTER(aOverrideBits); michael@0: NS_ENSURE_ARG_POINTER(aIsTemporary); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: *_retval = false; michael@0: *aOverrideBits = nsCertOverride::ob_None; michael@0: michael@0: nsAutoCString hostPort; michael@0: GetHostWithPort(aHostName, aPort, hostPort); michael@0: nsCertOverride settings; michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); michael@0: michael@0: if (!entry) michael@0: return NS_OK; michael@0: michael@0: settings = entry->mSettings; // copy michael@0: } michael@0: michael@0: *aOverrideBits = settings.mOverrideBits; michael@0: *aIsTemporary = settings.mIsTemporary; michael@0: michael@0: nsAutoCString fpStr; michael@0: nsresult rv; michael@0: michael@0: if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) { michael@0: rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr); michael@0: } michael@0: else { michael@0: rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr); michael@0: } michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: *_retval = settings.mFingerprint.Equals(fpStr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, int32_t aPort, michael@0: nsACString & aHashAlg, michael@0: nsACString & aFingerprint, michael@0: uint32_t *aOverrideBits, michael@0: bool *aIsTemporary, michael@0: bool *_found) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_found); michael@0: NS_ENSURE_ARG_POINTER(aIsTemporary); michael@0: NS_ENSURE_ARG_POINTER(aOverrideBits); michael@0: *_found = false; michael@0: *aOverrideBits = nsCertOverride::ob_None; michael@0: michael@0: nsAutoCString hostPort; michael@0: GetHostWithPort(aHostName, aPort, hostPort); michael@0: nsCertOverride settings; michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get()); michael@0: michael@0: if (entry) { michael@0: *_found = true; michael@0: settings = entry->mSettings; // copy michael@0: } michael@0: } michael@0: michael@0: if (*_found) { michael@0: *aOverrideBits = settings.mOverrideBits; michael@0: *aIsTemporary = settings.mIsTemporary; michael@0: aFingerprint = settings.mFingerprint; michael@0: aHashAlg = settings.mFingerprintAlgOID; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsCertOverrideService::AddEntryToList(const nsACString &aHostName, int32_t aPort, michael@0: nsIX509Cert *aCert, michael@0: const bool aIsTemporary, michael@0: const nsACString &fingerprintAlgOID, michael@0: const nsACString &fingerprint, michael@0: nsCertOverride::OverrideBits ob, michael@0: const nsACString &dbKey) michael@0: { michael@0: nsAutoCString hostPort; michael@0: GetHostWithPort(aHostName, aPort, hostPort); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get()); michael@0: michael@0: if (!entry) { michael@0: NS_ERROR("can't insert a null entry!"); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: entry->mHostWithPort = hostPort; michael@0: michael@0: nsCertOverride &settings = entry->mSettings; michael@0: settings.mAsciiHost = aHostName; michael@0: settings.mPort = aPort; michael@0: settings.mIsTemporary = aIsTemporary; michael@0: settings.mFingerprintAlgOID = fingerprintAlgOID; michael@0: settings.mFingerprint = fingerprint; michael@0: settings.mOverrideBits = ob; michael@0: settings.mDBKey = dbKey; michael@0: settings.mCert = aCert; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, int32_t aPort) michael@0: { michael@0: if (aPort == 0 && michael@0: aHostName.EqualsLiteral("all:temporary-certificates")) { michael@0: RemoveAllTemporaryOverrides(); michael@0: return NS_OK; michael@0: } michael@0: nsAutoCString hostPort; michael@0: GetHostWithPort(aHostName, aPort, hostPort); michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: mSettingsTable.RemoveEntry(hostPort.get()); michael@0: Write(); michael@0: } michael@0: SSL_ClearSessionCache(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::GetAllOverrideHostsWithPorts(uint32_t *aCount, michael@0: char16_t ***aHostsWithPortsArray) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: static bool michael@0: matchesDBKey(nsIX509Cert *cert, const char *match_dbkey) michael@0: { michael@0: char *dbkey = nullptr; michael@0: nsresult rv = cert->GetDbKey(&dbkey); michael@0: if (NS_FAILED(rv) || !dbkey) michael@0: return false; michael@0: michael@0: bool found_mismatch = false; michael@0: const char *key1 = dbkey; michael@0: const char *key2 = match_dbkey; michael@0: michael@0: // skip over any whitespace when comparing michael@0: while (*key1 && *key2) { michael@0: char c1 = *key1; michael@0: char c2 = *key2; michael@0: michael@0: switch (c1) { michael@0: case ' ': michael@0: case '\t': michael@0: case '\n': michael@0: case '\r': michael@0: ++key1; michael@0: continue; michael@0: } michael@0: michael@0: switch (c2) { michael@0: case ' ': michael@0: case '\t': michael@0: case '\n': michael@0: case '\r': michael@0: ++key2; michael@0: continue; michael@0: } michael@0: michael@0: if (c1 != c2) { michael@0: found_mismatch = true; michael@0: break; michael@0: } michael@0: michael@0: ++key1; michael@0: ++key2; michael@0: } michael@0: michael@0: PR_Free(dbkey); michael@0: return !found_mismatch; michael@0: } michael@0: michael@0: struct nsCertAndBoolsAndInt michael@0: { michael@0: nsIX509Cert *cert; michael@0: bool aCheckTemporaries; michael@0: bool aCheckPermanents; michael@0: uint32_t counter; michael@0: michael@0: SECOidTag mOidTagForStoringNewHashes; michael@0: nsCString mDottedOidForStoringNewHashes; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: FindMatchingCertCallback(nsCertOverrideEntry *aEntry, michael@0: void *aArg) michael@0: { michael@0: nsCertAndBoolsAndInt *cai = (nsCertAndBoolsAndInt *)aArg; michael@0: michael@0: if (cai && aEntry) michael@0: { michael@0: const nsCertOverride &settings = aEntry->mSettings; michael@0: bool still_ok = true; michael@0: michael@0: if ((settings.mIsTemporary && !cai->aCheckTemporaries) michael@0: || michael@0: (!settings.mIsTemporary && !cai->aCheckPermanents)) { michael@0: still_ok = false; michael@0: } michael@0: michael@0: if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) { michael@0: nsAutoCString cert_fingerprint; michael@0: nsresult rv; michael@0: if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) { michael@0: rv = GetCertFingerprintByOidTag(cai->cert, michael@0: cai->mOidTagForStoringNewHashes, cert_fingerprint); michael@0: } michael@0: else { michael@0: rv = GetCertFingerprintByDottedOidString(cai->cert, michael@0: settings.mFingerprintAlgOID, cert_fingerprint); michael@0: } michael@0: if (NS_SUCCEEDED(rv) && michael@0: settings.mFingerprint.Equals(cert_fingerprint)) { michael@0: cai->counter++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert, michael@0: bool aCheckTemporaries, michael@0: bool aCheckPermanents, michael@0: uint32_t *_retval) michael@0: { michael@0: NS_ENSURE_ARG(aCert); michael@0: NS_ENSURE_ARG(_retval); michael@0: michael@0: nsCertAndBoolsAndInt cai; michael@0: cai.cert = aCert; michael@0: cai.aCheckTemporaries = aCheckTemporaries; michael@0: cai.aCheckPermanents = aCheckPermanents; michael@0: cai.counter = 0; michael@0: cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes; michael@0: cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes; michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai); michael@0: } michael@0: *_retval = cai.counter; michael@0: return NS_OK; michael@0: } michael@0: michael@0: struct nsCertAndPointerAndCallback michael@0: { michael@0: nsIX509Cert *cert; michael@0: void *userdata; michael@0: nsCertOverrideService::CertOverrideEnumerator enumerator; michael@0: michael@0: SECOidTag mOidTagForStoringNewHashes; michael@0: nsCString mDottedOidForStoringNewHashes; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry, michael@0: void *aArg) michael@0: { michael@0: nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg; michael@0: michael@0: if (capac && aEntry) michael@0: { michael@0: const nsCertOverride &settings = aEntry->mSettings; michael@0: michael@0: if (!capac->cert) { michael@0: (*capac->enumerator)(settings, capac->userdata); michael@0: } michael@0: else { michael@0: if (matchesDBKey(capac->cert, settings.mDBKey.get())) { michael@0: nsAutoCString cert_fingerprint; michael@0: nsresult rv; michael@0: if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) { michael@0: rv = GetCertFingerprintByOidTag(capac->cert, michael@0: capac->mOidTagForStoringNewHashes, cert_fingerprint); michael@0: } michael@0: else { michael@0: rv = GetCertFingerprintByDottedOidString(capac->cert, michael@0: settings.mFingerprintAlgOID, cert_fingerprint); michael@0: } michael@0: if (NS_SUCCEEDED(rv) && michael@0: settings.mFingerprint.Equals(cert_fingerprint)) { michael@0: (*capac->enumerator)(settings, capac->userdata); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsresult michael@0: nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert, michael@0: CertOverrideEnumerator enumerator, michael@0: void *aUserData) michael@0: { michael@0: nsCertAndPointerAndCallback capac; michael@0: capac.cert = aCert; michael@0: capac.userdata = aUserData; michael@0: capac.enumerator = enumerator; michael@0: capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes; michael@0: capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes; michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, int32_t aPort, nsACString& _retval) michael@0: { michael@0: nsAutoCString hostPort(aHostName); michael@0: if (aPort == -1) { michael@0: aPort = 443; michael@0: } michael@0: if (!hostPort.IsEmpty()) { michael@0: hostPort.AppendLiteral(":"); michael@0: hostPort.AppendInt(aPort); michael@0: } michael@0: _retval.Assign(hostPort); michael@0: } michael@0: