From c3cd918c5cd57bd467b5d8676e4e827212663dcc Mon Sep 17 00:00:00 2001 From: Andreea Pavel Date: Wed, 20 Mar 2019 00:03:49 +0200 Subject: [PATCH] Backed out 2 changesets (bug 1429796) for failing xperf on a CLOSED TREE Backed out changeset b0d08863f7a5 (bug 1429796) Backed out changeset 1bd54f8dfd9e (bug 1429796) --- Cargo.lock | 15 - netwerk/base/nsNetUtil.cpp | 4 +- .../certverifier/NSSCertDBTrustDomain.cpp | 11 +- security/certverifier/NSSCertDBTrustDomain.h | 4 +- security/manager/ssl/CSTrustDomain.cpp | 12 +- security/manager/ssl/CSTrustDomain.h | 4 +- security/manager/ssl/CertBlocklist.cpp | 626 +++++++++++++++ security/manager/ssl/CertBlocklist.h | 89 +++ .../manager/ssl/ContentSignatureVerifier.h | 2 - security/manager/ssl/DataStorage.cpp | 1 - security/manager/ssl/NSSErrorsService.h | 2 - security/manager/ssl/cert_storage/Cargo.toml | 14 - .../ssl/cert_storage/src/cert_storage.cpp | 23 - .../ssl/cert_storage/src/cert_storage.h | 29 - security/manager/ssl/cert_storage/src/lib.rs | 732 ------------------ security/manager/ssl/components.conf | 8 +- security/manager/ssl/moz.build | 4 +- security/manager/ssl/nsICertBlocklist.idl | 64 ++ security/manager/ssl/nsICertStorage.idl | 116 --- security/manager/ssl/nsNSSModule.cpp | 7 +- .../mochitest/browser/browser_certViewer.js | 9 +- ...cert_storage.js => test_cert_blocklist.js} | 109 ++- security/manager/ssl/tests/unit/xpcshell.ini | 2 +- services/common/blocklist-clients.js | 33 +- toolkit/library/rust/shared/Cargo.toml | 1 - toolkit/library/rust/shared/lib.rs | 1 - xpcom/build/Services.py | 5 - 27 files changed, 920 insertions(+), 1007 deletions(-) create mode 100644 security/manager/ssl/CertBlocklist.cpp create mode 100644 security/manager/ssl/CertBlocklist.h delete mode 100644 security/manager/ssl/cert_storage/Cargo.toml delete mode 100644 security/manager/ssl/cert_storage/src/cert_storage.cpp delete mode 100644 security/manager/ssl/cert_storage/src/cert_storage.h delete mode 100644 security/manager/ssl/cert_storage/src/lib.rs create mode 100644 security/manager/ssl/nsICertBlocklist.idl delete mode 100644 security/manager/ssl/nsICertStorage.idl rename security/manager/ssl/tests/unit/{test_cert_storage.js => test_cert_blocklist.js} (73%) diff --git a/Cargo.lock b/Cargo.lock index b0ab3a582e4a..a1789743e6e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -376,20 +376,6 @@ name = "cc" version = "1.0.23" source = "git+https://github.com/glandium/cc-rs?branch=1.0.23-clang-cl-aarch64#2aa71628b1261b5515bd8668afca591669ba195d" -[[package]] -name = "cert_storage" -version = "0.0.1" -dependencies = [ - "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nserror 0.1.0", - "nsstring 0.1.0", - "rkv 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "style 0.0.1", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "xpcom 0.1.0", -] - [[package]] name = "cexpr" version = "0.3.3" @@ -1141,7 +1127,6 @@ dependencies = [ "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "audioipc-client 0.4.0", "audioipc-server 0.2.3", - "cert_storage 0.0.1", "cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "cubeb-pulse 0.2.0", "cubeb-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 982ab8f931f4..ac8093143c20 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -80,7 +80,7 @@ #include "nsHttpHandler.h" #include "nsNSSComponent.h" #include "nsIRedirectHistoryEntry.h" -#include "nsICertStorage.h" +#include "nsICertBlocklist.h" #include "nsICertOverrideService.h" #include "nsQueryObject.h" #include "mozIThirdPartyUtil.h" @@ -2572,7 +2572,7 @@ void net_EnsurePSMInit() { MOZ_ASSERT(NS_SUCCEEDED(rv)); nsCOMPtr sss = do_GetService(NS_SSSERVICE_CONTRACTID); - nsCOMPtr cbl = do_GetService(NS_CERTSTORAGE_CONTRACTID); + nsCOMPtr cbl = do_GetService(NS_CERTBLOCKLIST_CONTRACTID); nsCOMPtr cos = do_GetService(NS_CERTOVERRIDE_CONTRACTID); } diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index 2616378b3aa3..a756e4ddd782 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -14,7 +14,6 @@ #include "PublicKeyPinningService.h" #include "cert.h" #include "certdb.h" -#include "cert_storage/src/cert_storage.h" #include "mozilla/Assertions.h" #include "mozilla/Casting.h" #include "mozilla/Move.h" @@ -86,7 +85,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain( mBuiltChain(builtChain), mPinningTelemetryInfo(pinningTelemetryInfo), mHostname(hostname), - mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)), + mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)), mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED), mSCTListFromCertificate(), mSCTListFromOCSPStapling() {} @@ -196,7 +195,7 @@ Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, // The certificate blocklist currently only applies to TLS server // certificates. if (mCertDBTrustType == trustSSL) { - int16_t revocationState; + bool isCertRevoked; nsAutoCString encIssuer; nsAutoCString encSerial; @@ -210,13 +209,13 @@ Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, return Result::FATAL_ERROR_LIBRARY_FAILURE; } - nsrv = mCertBlocklist->GetRevocationState(encIssuer, encSerial, encSubject, - encPubKey, &revocationState); + nsrv = mCertBlocklist->IsCertRevoked(encIssuer, encSerial, encSubject, + encPubKey, &isCertRevoked); if (NS_FAILED(nsrv)) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } - if (revocationState == nsICertStorage::STATE_ENFORCE) { + if (isCertRevoked) { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("NSSCertDBTrustDomain: certificate is in blocklist")); return Result::ERROR_REVOKED_CERTIFICATE; diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index 6370cf61fabc..9ed16a356858 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -11,7 +11,7 @@ #include "ScopedNSSTypes.h" #include "mozilla/BasePrincipal.h" #include "mozilla/TimeStamp.h" -#include "nsICertStorage.h" +#include "nsICertBlocklist.h" #include "nsString.h" #include "mozpkix/pkixtypes.h" #include "secmodt.h" @@ -223,7 +223,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { UniqueCERTCertList& mBuiltChain; // non-owning PinningTelemetryInfo* mPinningTelemetryInfo; const char* mHostname; // non-owning - only used for pinning checks - nsCOMPtr mCertBlocklist; + nsCOMPtr mCertBlocklist; CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus; // Certificate Transparency data extracted during certificate verification UniqueSECItem mSCTListFromCertificate; diff --git a/security/manager/ssl/CSTrustDomain.cpp b/security/manager/ssl/CSTrustDomain.cpp index 3a01d30f7ee0..f17b2460a996 100644 --- a/security/manager/ssl/CSTrustDomain.cpp +++ b/security/manager/ssl/CSTrustDomain.cpp @@ -4,11 +4,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "cert_storage/src/cert_storage.h" #include "CSTrustDomain.h" #include "mozilla/Base64.h" #include "mozilla/Preferences.h" -#include "nsDirectoryServiceUtils.h" #include "nsNSSCertificate.h" #include "nsNSSComponent.h" #include "NSSCertDBTrustDomain.h" @@ -26,7 +24,7 @@ static LazyLogModule gTrustDomainPRLog("CSTrustDomain"); CSTrustDomain::CSTrustDomain(UniqueCERTCertList& certChain) : mCertChain(certChain), - mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)) {} + mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) {} Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, @@ -55,14 +53,14 @@ Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, return Result::FATAL_ERROR_LIBRARY_FAILURE; } - int16_t revocationState; - nsrv = mCertBlocklist->GetRevocationState(encIssuer, encSerial, encSubject, - encPubKey, &revocationState); + bool isCertRevoked; + nsrv = mCertBlocklist->IsCertRevoked(encIssuer, encSerial, encSubject, + encPubKey, &isCertRevoked); if (NS_FAILED(nsrv)) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } - if (revocationState == nsICertStorage::STATE_ENFORCE) { + if (isCertRevoked) { CSTrust_LOG(("CSTrustDomain: certificate is revoked\n")); return Result::ERROR_REVOKED_CERTIFICATE; } diff --git a/security/manager/ssl/CSTrustDomain.h b/security/manager/ssl/CSTrustDomain.h index 3cdf0bf7088d..cef838b2b607 100644 --- a/security/manager/ssl/CSTrustDomain.h +++ b/security/manager/ssl/CSTrustDomain.h @@ -11,7 +11,7 @@ #include "mozilla/StaticMutex.h" #include "mozilla/UniquePtr.h" #include "nsDebug.h" -#include "nsICertStorage.h" +#include "nsICertBlocklist.h" #include "nsIX509CertDB.h" #include "ScopedNSSTypes.h" @@ -73,7 +73,7 @@ class CSTrustDomain final : public mozilla::pkix::TrustDomain { private: /*out*/ UniqueCERTCertList& mCertChain; - nsCOMPtr mCertBlocklist; + nsCOMPtr mCertBlocklist; }; } // namespace psm diff --git a/security/manager/ssl/CertBlocklist.cpp b/security/manager/ssl/CertBlocklist.cpp new file mode 100644 index 000000000000..e01378ce6605 --- /dev/null +++ b/security/manager/ssl/CertBlocklist.cpp @@ -0,0 +1,626 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CertBlocklist.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Base64.h" +#include "mozilla/Casting.h" +#include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDependentString.h" +#include "nsDirectoryServiceUtils.h" +#include "nsICryptoHash.h" +#include "nsIFileStreams.h" +#include "nsILineInputStream.h" +#include "nsISafeOutputStream.h" +#include "nsIX509Cert.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsPromiseFlatString.h" +#include "nsTHashtable.h" +#include "nsThreadUtils.h" +#include "mozpkix/Input.h" +#include "prtime.h" + +NS_IMPL_ISUPPORTS(CertBlocklist, nsICertBlocklist) + +using namespace mozilla; +using namespace mozilla::pkix; + +#define PREF_BACKGROUND_UPDATE_TIMER \ + "app.update.lastUpdateTime.blocklist-background-update-timer" +#define PREF_BLOCKLIST_ONECRL_CHECKED "services.blocklist.onecrl.checked" +#define PREF_MAX_STALENESS_IN_SECONDS \ + "security.onecrl.maximum_staleness_in_seconds" + +static LazyLogModule gCertBlockPRLog("CertBlock"); + +uint32_t CertBlocklist::sLastBlocklistUpdate = 0U; +uint32_t CertBlocklist::sMaxStaleness = 0U; + +CertBlocklistItem::CertBlocklistItem(const uint8_t* DNData, size_t DNLength, + const uint8_t* otherData, + size_t otherLength, + CertBlocklistItemMechanism itemMechanism) + : mIsCurrent(false), mItemMechanism(itemMechanism) { + mDNData = new uint8_t[DNLength]; + memcpy(mDNData, DNData, DNLength); + mDNLength = DNLength; + + mOtherData = new uint8_t[otherLength]; + memcpy(mOtherData, otherData, otherLength); + mOtherLength = otherLength; +} + +CertBlocklistItem::CertBlocklistItem(const CertBlocklistItem& aItem) { + mDNLength = aItem.mDNLength; + mDNData = new uint8_t[mDNLength]; + memcpy(mDNData, aItem.mDNData, mDNLength); + + mOtherLength = aItem.mOtherLength; + mOtherData = new uint8_t[mOtherLength]; + memcpy(mOtherData, aItem.mOtherData, mOtherLength); + + mItemMechanism = aItem.mItemMechanism; + + mIsCurrent = aItem.mIsCurrent; +} + +CertBlocklistItem::~CertBlocklistItem() { + delete[] mDNData; + delete[] mOtherData; +} + +nsresult CertBlocklistItem::ToBase64(nsACString& b64DNOut, + nsACString& b64OtherOut) { + nsDependentCSubstring DNString(BitwiseCast(mDNData), + mDNLength); + nsDependentCSubstring otherString(BitwiseCast(mOtherData), + mOtherLength); + nsresult rv = Base64Encode(DNString, b64DNOut); + if (NS_FAILED(rv)) { + return rv; + } + rv = Base64Encode(otherString, b64OtherOut); + return rv; +} + +bool CertBlocklistItem::operator==(const CertBlocklistItem& aItem) const { + if (aItem.mItemMechanism != mItemMechanism) { + return false; + } + if (aItem.mDNLength != mDNLength || aItem.mOtherLength != mOtherLength) { + return false; + } + return memcmp(aItem.mDNData, mDNData, mDNLength) == 0 && + memcmp(aItem.mOtherData, mOtherData, mOtherLength) == 0; +} + +uint32_t CertBlocklistItem::Hash() const { + uint32_t hash; + // there's no requirement for a serial to be as large as the size of the hash + // key; if it's smaller, fall back to the first octet (otherwise, the last + // four) + if (mItemMechanism == BlockByIssuerAndSerial && + mOtherLength >= sizeof(hash)) { + memcpy(&hash, mOtherData + mOtherLength - sizeof(hash), sizeof(hash)); + } else { + hash = *mOtherData; + } + return hash; +} + +CertBlocklist::CertBlocklist() + : mMutex("CertBlocklist::mMutex"), + mModified(false), + mBackingFileIsInitialized(false), + mBackingFile(nullptr) {} + +CertBlocklist::~CertBlocklist() { + Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged, + PREF_MAX_STALENESS_IN_SECONDS, this); + Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged, + PREF_BLOCKLIST_ONECRL_CHECKED, this); +} + +nsresult CertBlocklist::Init() { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, ("CertBlocklist::Init")); + + // Init must be on main thread for getting the profile directory + if (!NS_IsMainThread()) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::Init - called off main thread")); + return NS_ERROR_NOT_SAME_THREAD; + } + + // Register preference callbacks + nsresult rv = Preferences::RegisterCallbackAndCall( + CertBlocklist::PreferenceChanged, PREF_MAX_STALENESS_IN_SECONDS, this); + if (NS_FAILED(rv)) { + return rv; + } + rv = Preferences::RegisterCallbackAndCall( + CertBlocklist::PreferenceChanged, PREF_BLOCKLIST_ONECRL_CHECKED, this); + if (NS_FAILED(rv)) { + return rv; + } + + // Get the profile directory + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mBackingFile)); + if (NS_FAILED(rv) || !mBackingFile) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::Init - couldn't get profile dir")); + // Since we're returning NS_OK here, set mBackingFile to a safe value. + // (We need initialization to succeed and CertBlocklist to be in a + // well-defined state if the profile directory doesn't exist.) + mBackingFile = nullptr; + return NS_OK; + } + rv = mBackingFile->Append(NS_LITERAL_STRING("revocations.txt")); + if (NS_FAILED(rv)) { + return rv; + } + nsAutoCString path; + rv = mBackingFile->GetPersistentDescriptor(path); + if (NS_FAILED(rv)) { + return rv; + } + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::Init certList path: %s", path.get())); + + return NS_OK; +} + +nsresult CertBlocklist::EnsureBackingFileInitialized(MutexAutoLock& lock) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::EnsureBackingFileInitialized")); + if (mBackingFileIsInitialized || !mBackingFile) { + return NS_OK; + } + + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::EnsureBackingFileInitialized - not initialized")); + + bool exists = false; + nsresult rv = mBackingFile->Exists(&exists); + if (NS_FAILED(rv)) { + return rv; + } + if (!exists) { + MOZ_LOG( + gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::EnsureBackingFileInitialized no revocations file")); + return NS_OK; + } + + // Load the revocations file into the cert blocklist + nsCOMPtr fileStream( + do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + return rv; + } + + rv = fileStream->Init(mBackingFile, -1, -1, false); + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr lineStream(do_QueryInterface(fileStream, &rv)); + nsAutoCString line; + nsAutoCString DN; + nsAutoCString other; + CertBlocklistItemMechanism mechanism; + // read in the revocations file. The file format is as follows: each line + // contains a comment, base64 encoded DER for a DN, base64 encoded DER for a + // serial number or a Base64 encoded SHA256 hash of a public key. Comment + // lines start with '#', serial number lines, ' ' (a space), public key hashes + // with '\t' (a tab) and anything else is assumed to be a DN. + bool more = true; + do { + rv = lineStream->ReadLine(line, &more); + if (NS_FAILED(rv)) { + break; + } + // ignore comments and empty lines + if (line.IsEmpty() || line.First() == '#') { + continue; + } + if (line.First() != ' ' && line.First() != '\t') { + DN = line; + continue; + } + other = line; + if (line.First() == ' ') { + mechanism = BlockByIssuerAndSerial; + } else { + mechanism = BlockBySubjectAndPubKey; + } + other.Trim(" \t", true, false, false); + // Serial numbers and public key hashes 'belong' to the last DN line seen; + // if no DN has been seen, the serial number or public key hash is ignored. + if (DN.IsEmpty() || other.IsEmpty()) { + continue; + } + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::EnsureBackingFileInitialized adding: %s %s", + DN.get(), other.get())); + + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::EnsureBackingFileInitialized - pre-decode")); + + rv = AddRevokedCertInternal(DN, other, mechanism, CertOldFromLocalCache, + lock); + + if (NS_FAILED(rv)) { + // we warn here, rather than abandoning, since we need to + // ensure that as many items as possible are read + MOZ_LOG( + gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::EnsureBackingFileInitialized adding revoked cert " + "failed")); + } + } while (more); + mBackingFileIsInitialized = true; + return NS_OK; +} + +NS_IMETHODIMP +CertBlocklist::RevokeCertBySubjectAndPubKey(const nsACString& aSubject, + const nsACString& aPubKeyHash) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::RevokeCertBySubjectAndPubKey - subject is: %s and " + "pubKeyHash: %s", + PromiseFlatCString(aSubject).get(), + PromiseFlatCString(aPubKeyHash).get())); + MutexAutoLock lock(mMutex); + + return AddRevokedCertInternal(aSubject, aPubKeyHash, BlockBySubjectAndPubKey, + CertNewFromBlocklist, lock); +} + +NS_IMETHODIMP +CertBlocklist::RevokeCertByIssuerAndSerial(const nsACString& aIssuer, + const nsACString& aSerialNumber) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::RevokeCertByIssuerAndSerial - issuer is: %s and " + "serial: %s", + PromiseFlatCString(aIssuer).get(), + PromiseFlatCString(aSerialNumber).get())); + MutexAutoLock lock(mMutex); + + return AddRevokedCertInternal(aIssuer, aSerialNumber, BlockByIssuerAndSerial, + CertNewFromBlocklist, lock); +} + +nsresult CertBlocklist::AddRevokedCertInternal( + const nsACString& aEncodedDN, const nsACString& aEncodedOther, + CertBlocklistItemMechanism aMechanism, CertBlocklistItemState aItemState, + MutexAutoLock& /*proofOfLock*/) { + nsCString decodedDN; + nsCString decodedOther; + + nsresult rv = Base64Decode(aEncodedDN, decodedDN); + if (NS_FAILED(rv)) { + return rv; + } + rv = Base64Decode(aEncodedOther, decodedOther); + if (NS_FAILED(rv)) { + return rv; + } + + CertBlocklistItem item( + BitwiseCast(decodedDN.get()), + decodedDN.Length(), + BitwiseCast(decodedOther.get()), + decodedOther.Length(), aMechanism); + + if (aItemState == CertNewFromBlocklist) { + // We want SaveEntries to be a no-op if no new entries are added. + nsGenericHashKey* entry = mBlocklist.GetEntry(item); + if (!entry) { + mModified = true; + } else { + // Ensure that any existing item is replaced by a fresh one so we can + // use mIsCurrent to decide which entries to write out. + mBlocklist.RemoveEntry(entry); + } + item.mIsCurrent = true; + } + mBlocklist.PutEntry(item); + + return NS_OK; +} + +// Write a line for a given string in the output stream +nsresult WriteLine(nsIOutputStream* outputStream, const nsACString& string) { + nsAutoCString line(string); + line.Append('\n'); + + const char* data = line.get(); + uint32_t length = line.Length(); + nsresult rv = NS_OK; + while (NS_SUCCEEDED(rv) && length) { + uint32_t bytesWritten = 0; + rv = outputStream->Write(data, length, &bytesWritten); + if (NS_FAILED(rv)) { + return rv; + } + // if no data is written, something is wrong + if (!bytesWritten) { + return NS_ERROR_FAILURE; + } + length -= bytesWritten; + data += bytesWritten; + } + return rv; +} + +// void saveEntries(); +// Store the blockist in a text file containing base64 encoded issuers and +// serial numbers. +// +// Each item is stored on a separate line; each issuer is followed by its +// revoked serial numbers, indented by one space. +// +// lines starting with a # character are ignored +NS_IMETHODIMP +CertBlocklist::SaveEntries() { + MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, + ("CertBlocklist::SaveEntries - not initialized")); + MutexAutoLock lock(mMutex); + if (!mModified) { + return NS_OK; + } + + nsresult rv = EnsureBackingFileInitialized(lock); + if (NS_FAILED(rv)) { + return rv; + } + + if (!mBackingFile) { + // We allow this to succeed with no profile directory for tests + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::SaveEntries no file in profile to write to")); + return NS_OK; + } + + // Data needed for writing blocklist items out to the revocations file + IssuerTable issuerTable; + BlocklistStringSet issuers; + nsCOMPtr outputStream; + + rv = NS_NewAtomicFileOutputStream(getter_AddRefs(outputStream), mBackingFile, + -1, -1, 0); + if (NS_FAILED(rv)) { + return rv; + } + + rv = WriteLine(outputStream, + NS_LITERAL_CSTRING("# Auto generated contents. Do not edit.")); + if (NS_FAILED(rv)) { + return rv; + } + + // Sort blocklist items into lists of serials for each issuer + for (auto iter = mBlocklist.Iter(); !iter.Done(); iter.Next()) { + CertBlocklistItem item = iter.Get()->GetKey(); + if (!item.mIsCurrent) { + continue; + } + + nsAutoCString encDN; + nsAutoCString encOther; + + nsresult rv = item.ToBase64(encDN, encOther); + if (NS_FAILED(rv)) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::SaveEntries writing revocation data failed")); + return NS_ERROR_FAILURE; + } + + // If it's a subject / public key block, write it straight out + if (item.mItemMechanism == BlockBySubjectAndPubKey) { + WriteLine(outputStream, encDN); + WriteLine(outputStream, NS_LITERAL_CSTRING("\t") + encOther); + continue; + } + + // Otherwise, we have to group entries by issuer + issuers.PutEntry(encDN); + BlocklistStringSet* issuerSet = issuerTable.Get(encDN); + if (!issuerSet) { + issuerSet = new BlocklistStringSet(); + issuerTable.Put(encDN, issuerSet); + } + issuerSet->PutEntry(encOther); + } + + for (auto iter = issuers.Iter(); !iter.Done(); iter.Next()) { + nsCStringHashKey* hashKey = iter.Get(); + nsAutoPtr issuerSet; + issuerTable.Remove(hashKey->GetKey(), &issuerSet); + + nsresult rv = WriteLine(outputStream, hashKey->GetKey()); + if (NS_FAILED(rv)) { + break; + } + + // Write serial data to the output stream + for (auto iter = issuerSet->Iter(); !iter.Done(); iter.Next()) { + nsresult rv = WriteLine(outputStream, + NS_LITERAL_CSTRING(" ") + iter.Get()->GetKey()); + if (NS_FAILED(rv)) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::SaveEntries writing revocation data failed")); + return NS_ERROR_FAILURE; + } + } + } + + nsCOMPtr safeStream = do_QueryInterface(outputStream); + MOZ_ASSERT(safeStream, "expected a safe output stream!"); + if (!safeStream) { + return NS_ERROR_FAILURE; + } + rv = safeStream->Finish(); + if (NS_FAILED(rv)) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::SaveEntries saving revocation data failed")); + return rv; + } + mModified = false; + return NS_OK; +} + +NS_IMETHODIMP +CertBlocklist::IsCertRevoked(const nsACString& aIssuerString, + const nsACString& aSerialNumberString, + const nsACString& aSubjectString, + const nsACString& aPubKeyString, bool* _retval) { + MutexAutoLock lock(mMutex); + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, ("CertBlocklist::IsCertRevoked")); + + nsCString decodedIssuer; + nsCString decodedSerial; + nsCString decodedSubject; + nsCString decodedPubKey; + + nsresult rv = Base64Decode(aIssuerString, decodedIssuer); + if (NS_FAILED(rv)) { + return rv; + } + rv = Base64Decode(aSerialNumberString, decodedSerial); + if (NS_FAILED(rv)) { + return rv; + } + rv = Base64Decode(aSubjectString, decodedSubject); + if (NS_FAILED(rv)) { + return rv; + } + rv = Base64Decode(aPubKeyString, decodedPubKey); + if (NS_FAILED(rv)) { + return rv; + } + + rv = EnsureBackingFileInitialized(lock); + if (NS_FAILED(rv)) { + return rv; + } + + CertBlocklistItem issuerSerial( + BitwiseCast(decodedIssuer.get()), + decodedIssuer.Length(), + BitwiseCast(decodedSerial.get()), + decodedSerial.Length(), BlockByIssuerAndSerial); + + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsCertRevoked issuer %s - serial %s", + PromiseFlatCString(aIssuerString).get(), + PromiseFlatCString(aSerialNumberString).get())); + + *_retval = mBlocklist.Contains(issuerSerial); + + if (*_retval) { + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("certblocklist::IsCertRevoked found by issuer / serial")); + return NS_OK; + } + + nsCOMPtr crypto; + crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + + rv = crypto->Init(nsICryptoHash::SHA256); + if (NS_FAILED(rv)) { + return rv; + } + + rv = crypto->Update( + BitwiseCast(decodedPubKey.get()), + decodedPubKey.Length()); + if (NS_FAILED(rv)) { + return rv; + } + + nsCString hashString; + rv = crypto->Finish(false, hashString); + if (NS_FAILED(rv)) { + return rv; + } + + CertBlocklistItem subjectPubKey( + BitwiseCast(decodedSubject.get()), + decodedSubject.Length(), + BitwiseCast(hashString.get()), + hashString.Length(), BlockBySubjectAndPubKey); + + nsCString encodedHash; + rv = Base64Encode(hashString, encodedHash); + if (NS_FAILED(rv)) { + return rv; + } + + MOZ_LOG( + gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsCertRevoked subject %s - pubKeyHash %s (pubKey %s)", + PromiseFlatCString(aSubjectString).get(), + PromiseFlatCString(encodedHash).get(), + PromiseFlatCString(aPubKeyString).get())); + *_retval = mBlocklist.Contains(subjectPubKey); + + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsCertRevoked by subject / pubkey? %s", + *_retval ? "true" : "false")); + + return NS_OK; +} + +NS_IMETHODIMP +CertBlocklist::IsBlocklistFresh(bool* _retval) { + MutexAutoLock lock(mMutex); + *_retval = false; + + uint32_t now = uint32_t(PR_Now() / PR_USEC_PER_SEC); + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsBlocklistFresh ? lastUpdate is %i", + sLastBlocklistUpdate)); + + if (now > sLastBlocklistUpdate) { + int64_t interval = now - sLastBlocklistUpdate; + MOZ_LOG( + gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsBlocklistFresh we're after the last BlocklistUpdate " + "interval is %" PRId64 ", staleness %u", + interval, sMaxStaleness)); + *_retval = sMaxStaleness > interval; + } + MOZ_LOG( + gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsBlocklistFresh ? %s", *_retval ? "true" : "false")); + return NS_OK; +} + +/* static */ +void CertBlocklist::PreferenceChanged(const char* aPref, + CertBlocklist* aBlocklist) + +{ + MutexAutoLock lock(aBlocklist->mMutex); + + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::PreferenceChanged %s changed", aPref)); + if (strcmp(aPref, PREF_BLOCKLIST_ONECRL_CHECKED) == 0) { + sLastBlocklistUpdate = + Preferences::GetUint(PREF_BLOCKLIST_ONECRL_CHECKED, uint32_t(0)); + } else if (strcmp(aPref, PREF_MAX_STALENESS_IN_SECONDS) == 0) { + sMaxStaleness = + Preferences::GetUint(PREF_MAX_STALENESS_IN_SECONDS, uint32_t(0)); + } +} diff --git a/security/manager/ssl/CertBlocklist.h b/security/manager/ssl/CertBlocklist.h new file mode 100644 index 000000000000..bef8f2346e1d --- /dev/null +++ b/security/manager/ssl/CertBlocklist.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CertBlocklist_h +#define CertBlocklist_h + +#include "mozilla/Mutex.h" +#include "nsCOMPtr.h" +#include "nsClassHashtable.h" +#include "nsICertBlocklist.h" +#include "nsIOutputStream.h" +#include "nsIX509CertDB.h" +#include "nsString.h" +#include "nsTHashtable.h" +#include "mozpkix/Input.h" + +#define NS_CERT_BLOCKLIST_CID \ + { \ + 0x11aefd53, 0x2fbb, 0x4c92, { \ + 0xa0, 0xc1, 0x05, 0x32, 0x12, 0xae, 0x42, 0xd0 \ + } \ + } + +enum CertBlocklistItemMechanism { + BlockByIssuerAndSerial, + BlockBySubjectAndPubKey +}; + +enum CertBlocklistItemState { CertNewFromBlocklist, CertOldFromLocalCache }; + +class CertBlocklistItem { + public: + CertBlocklistItem(const uint8_t* DNData, size_t DNLength, + const uint8_t* otherData, size_t otherLength, + CertBlocklistItemMechanism itemMechanism); + CertBlocklistItem(const CertBlocklistItem& aItem); + ~CertBlocklistItem(); + nsresult ToBase64(nsACString& b64IssuerOut, nsACString& b64SerialOut); + bool operator==(const CertBlocklistItem& aItem) const; + uint32_t Hash() const; + bool mIsCurrent; + CertBlocklistItemMechanism mItemMechanism; + + private: + size_t mDNLength; + uint8_t* mDNData; + size_t mOtherLength; + uint8_t* mOtherData; +}; + +typedef nsGenericHashKey BlocklistItemKey; +typedef nsTHashtable BlocklistTable; +typedef nsTHashtable BlocklistStringSet; +typedef nsClassHashtable IssuerTable; + +class CertBlocklist : public nsICertBlocklist { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICERTBLOCKLIST + CertBlocklist(); + nsresult Init(); + + private: + BlocklistTable mBlocklist; + nsresult AddRevokedCertInternal(const nsACString& aEncodedDN, + const nsACString& aEncodedOther, + CertBlocklistItemMechanism aMechanism, + CertBlocklistItemState aItemState, + mozilla::MutexAutoLock& /*proofOfLock*/); + mozilla::Mutex mMutex; + bool mModified; + bool mBackingFileIsInitialized; + // call EnsureBackingFileInitialized before operations that read or + // modify CertBlocklist data + nsresult EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock); + nsCOMPtr mBackingFile; + + protected: + static void PreferenceChanged(const char* aPref, CertBlocklist* aBlocklist); + static uint32_t sLastBlocklistUpdate; + static uint32_t sLastKintoUpdate; + static uint32_t sMaxStaleness; + static bool sUseAMO; + virtual ~CertBlocklist(); +}; + +#endif // CertBlocklist_h diff --git a/security/manager/ssl/ContentSignatureVerifier.h b/security/manager/ssl/ContentSignatureVerifier.h index 058df982bcff..36de38370dfc 100644 --- a/security/manager/ssl/ContentSignatureVerifier.h +++ b/security/manager/ssl/ContentSignatureVerifier.h @@ -9,10 +9,8 @@ #include "cert.h" #include "CSTrustDomain.h" -#include "nsDirectoryServiceUtils.h" #include "nsIContentSignatureVerifier.h" #include "nsIStreamListener.h" -#include "nsNetUtil.h" #include "nsString.h" #include "ScopedNSSTypes.h" diff --git a/security/manager/ssl/DataStorage.cpp b/security/manager/ssl/DataStorage.cpp index 1296b5b0b4ab..28919af9d751 100644 --- a/security/manager/ssl/DataStorage.cpp +++ b/security/manager/ssl/DataStorage.cpp @@ -18,7 +18,6 @@ #include "mozilla/Unused.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" -#include "nsIFileStreams.h" #include "nsIMemoryReporter.h" #include "nsIObserverService.h" #include "nsITimer.h" diff --git a/security/manager/ssl/NSSErrorsService.h b/security/manager/ssl/NSSErrorsService.h index 9e765e8a214e..d315a25d8a7b 100644 --- a/security/manager/ssl/NSSErrorsService.h +++ b/security/manager/ssl/NSSErrorsService.h @@ -8,8 +8,6 @@ #include "nsINSSErrorsService.h" #include "mozilla/Attributes.h" #include "nsCOMPtr.h" -#include "nsILineInputStream.h" -#include "nsISafeOutputStream.h" #include "nsIStringBundle.h" #include "prerror.h" diff --git a/security/manager/ssl/cert_storage/Cargo.toml b/security/manager/ssl/cert_storage/Cargo.toml deleted file mode 100644 index 415fc7415a9a..000000000000 --- a/security/manager/ssl/cert_storage/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "cert_storage" -version = "0.0.1" -authors = ["Dana Keeler ", "Mark Goodwin mainThread; - nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread)); - if (NS_FAILED(rv)) { - return rv; - } - - mozilla::SyncRunnable::DispatchToThread( - mainThread, new mozilla::SyncRunnable( - NS_NewRunnableFunction("psm::Constructor", [&]() { - rv = cert_storage_constructor(outer, iid, result); - }))); - return rv; -} \ No newline at end of file diff --git a/security/manager/ssl/cert_storage/src/cert_storage.h b/security/manager/ssl/cert_storage/src/cert_storage.h deleted file mode 100644 index f46c09dadfef..000000000000 --- a/security/manager/ssl/cert_storage/src/cert_storage.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef _cert_storage_h_ -#define _cert_storage_h_ - -#include "nsISupportsUtils.h" // for nsresult, etc. -#include "mozilla/SyncRunnable.h" - -// {16e5c837-f877-4e23-9c64-eddf905e30e6} -#define NS_CERT_STORAGE_CID \ - { \ - 0x16e5c837, 0xf877, 0x4e23, { \ - 0x9c, 0x64, 0xed, 0xdf, 0x90, 0x5e, 0x30, 0xe6 \ - } \ - } - -extern "C" { -nsresult cert_storage_constructor(nsISupports* outer, REFNSIID iid, - void** result); -}; - -nsresult construct_cert_storage(nsISupports* outer, REFNSIID iid, - void** result); - -#endif // _cert_storage_h_ \ No newline at end of file diff --git a/security/manager/ssl/cert_storage/src/lib.rs b/security/manager/ssl/cert_storage/src/lib.rs deleted file mode 100644 index d7a46b4c3202..000000000000 --- a/security/manager/ssl/cert_storage/src/lib.rs +++ /dev/null @@ -1,732 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -extern crate base64; -extern crate nserror; -extern crate nsstring; -extern crate rkv; -extern crate sha2; -extern crate time; -#[macro_use] -extern crate xpcom; -extern crate style; - -use nsstring::{nsACString, nsAString, nsCStr, nsCString, nsString}; -use sha2::{Digest, Sha256}; -use std::collections::HashMap; -use std::ffi::{CStr, CString}; -use std::fmt::Display; -use std::fs::{create_dir_all, remove_file, File}; -use std::io::{BufRead, BufReader}; -use std::os::raw::c_char; -use std::path::PathBuf; -use std::slice; -use std::str; -use std::sync::RwLock; -use std::time::{Duration, SystemTime}; -use style::gecko_bindings::structs::nsresult; -use xpcom::interfaces::{nsICertStorage, nsIFile, nsIObserver, nsIPrefBranch, nsISupports}; -use xpcom::{nsIID, GetterAddrefs, RefPtr, XpCom}; - -use rkv::{Rkv, SingleStore, StoreOptions, Value}; - -const PREFIX_REV_IS: &str = "is"; -const PREFIX_REV_SPK: &str = "spk"; -const PREFIX_CRLITE: &str = "crlite"; -const PREFIX_WL: &str = "wl"; - -fn make_key(prefix: &str, part_a: &[u8], part_b: &[u8]) -> Vec { - let mut key = prefix.as_bytes().to_owned(); - key.extend_from_slice(part_a); - key.extend_from_slice(part_b); - key -} - -#[allow(non_camel_case_types, non_snake_case)] - -/// `SecurityStateError` is a type to represent errors in accessing or -/// modifying security state. -#[derive(Debug)] -pub struct SecurityStateError { - message: String, -} - -impl From for SecurityStateError { - /// Creates a new instance of `SecurityStateError` from something that - /// implements the `Display` trait. - fn from(err: T) -> SecurityStateError { - SecurityStateError { - message: format!("{}", err), - } - } -} - -/// `SecurityState` -pub struct SecurityState { - env: Rkv, - store: SingleStore, - int_prefs: HashMap, -} - -impl SecurityState { - pub fn new(profile_path: PathBuf) -> Result { - let mut store_path = profile_path.clone(); - store_path.push("security_state"); - - create_dir_all(store_path.as_path())?; - let env = Rkv::new(store_path.as_path())?; - let mut options = StoreOptions::create(); - options.create = true; - let store = env.open_single("cert_storage", options)?; - let mut ss = SecurityState { - env: env, - store: store, - int_prefs: HashMap::new(), - }; - - let mut revocations_path = profile_path; - revocations_path.push("revocations.txt"); - - // if the profile has a revocations.txt, migrate it and remove the file - if revocations_path.exists() { - ss.migrate(&revocations_path)?; - remove_file(revocations_path)?; - } - Ok(ss) - } - - fn migrate(&mut self, revocations_path: &PathBuf) -> Result<(), SecurityStateError> { - let f = File::open(revocations_path)?; - let file = BufReader::new(f); - - // Add the data from revocations.txt - let mut dn: Option> = None; - for line in file.lines() { - let l = match line.map_err(|_| SecurityStateError::from("io error reading line data")) { - Ok(data) => data, - Err(e) => return Err(e), - }; - if l.len() == 0 || l.starts_with("#") { - continue; - } - let leading_char = match l.chars().next() { - Some(c) => c, - None => { - return Err(SecurityStateError::from( - "couldn't get char from non-empty str?", - )); - } - }; - // In future, we can maybe log migration failures. For now, ignore decoding and storage - // errors and attempt to continue. - // Check if we have a new DN - if leading_char != '\t' && leading_char != ' ' { - if let Ok(decoded_dn) = base64::decode(&l) { - dn = Some(decoded_dn); - } - continue; - } - let l_sans_prefix = match base64::decode(&l[1..]) { - Ok(decoded) => decoded, - Err(_) => continue, - }; - if let Some(name) = &dn { - if leading_char == '\t' { - let _ = self.set_revocation_by_subject_and_pub_key( - name, - &l_sans_prefix, - nsICertStorage::STATE_ENFORCE as i16, - ); - } else { - let _ = self.set_revocation_by_issuer_and_serial( - name, - &l_sans_prefix, - nsICertStorage::STATE_ENFORCE as i16, - ); - } - } - } - - Ok(()) - } - - fn write_entry(&mut self, key: &[u8], value: i16) -> Result<(), SecurityStateError> { - let mut writer = self.env.write()?; - self.store - .put(&mut writer, key, &Value::I64(value as i64))?; - writer.commit()?; - Ok(()) - } - - fn read_entry(&self, key: &[u8]) -> Result, SecurityStateError> { - let reader = self.env.read()?; - match self.store.get(&reader, key) { - Ok(Some(Value::I64(i))) - if i <= (std::i16::MAX as i64) && i >= (std::i16::MIN as i64) => - { - Ok(Some(i as i16)) - } - Ok(None) => Ok(None), - _ => Err(SecurityStateError::from( - "There was a problem getting the value", - )), - } - } - - pub fn set_revocation_by_issuer_and_serial( - &mut self, - issuer: &[u8], - serial: &[u8], - state: i16, - ) -> Result<(), SecurityStateError> { - self.write_entry(&make_key(PREFIX_REV_IS, issuer, serial), state) - } - - pub fn set_revocation_by_subject_and_pub_key( - &mut self, - subject: &[u8], - pub_key_hash: &[u8], - state: i16, - ) -> Result<(), SecurityStateError> { - self.write_entry(&make_key(PREFIX_REV_SPK, subject, pub_key_hash), state) - } - - pub fn set_enrollment( - &mut self, - issuer: &[u8], - serial: &[u8], - state: i16, - ) -> Result<(), SecurityStateError> { - self.write_entry(&make_key(PREFIX_CRLITE, issuer, serial), state) - } - - pub fn set_whitelist( - &mut self, - issuer: &[u8], - serial: &[u8], - state: i16, - ) -> Result<(), SecurityStateError> { - self.write_entry(&make_key(PREFIX_WL, issuer, serial), state) - } - - pub fn get_revocation_state( - &self, - issuer: &[u8], - serial: &[u8], - subject: &[u8], - pub_key: &[u8], - ) -> Result { - let mut digest = Sha256::default(); - digest.input(pub_key); - let pub_key_hash = digest.result(); - - let subject_pubkey = make_key(PREFIX_REV_SPK, subject, &pub_key_hash); - let issuer_serial = make_key(PREFIX_REV_IS, issuer, serial); - - let st: i16 = match self.read_entry(&issuer_serial) { - Ok(Some(value)) => value, - Ok(None) => nsICertStorage::STATE_UNSET as i16, - Err(_) => { - return Err(SecurityStateError::from( - "problem reading revocation state (from issuer / serial)", - )); - } - }; - - if st != nsICertStorage::STATE_UNSET as i16 { - return Ok(st); - } - - match self.read_entry(&subject_pubkey) { - Ok(Some(value)) => Ok(value), - Ok(None) => Ok(nsICertStorage::STATE_UNSET as i16), - Err(_) => { - return Err(SecurityStateError::from( - "problem reading revocation state (from subject / pubkey)", - )); - } - } - } - - pub fn get_enrollment_state( - &self, - issuer: &[u8], - serial: &[u8], - ) -> Result { - let issuer_serial = make_key(PREFIX_CRLITE, issuer, serial); - match self.read_entry(&issuer_serial) { - Ok(Some(value)) => Ok(value), - Ok(None) => Ok(nsICertStorage::STATE_UNSET as i16), - Err(_) => return Err(SecurityStateError::from("problem reading enrollment state")), - } - } - - pub fn get_whitelist_state( - &self, - issuer: &[u8], - serial: &[u8], - ) -> Result { - let issuer_serial = make_key(PREFIX_WL, issuer, serial); - match self.read_entry(&issuer_serial) { - Ok(Some(value)) => Ok(value), - Ok(None) => Ok(nsICertStorage::STATE_UNSET as i16), - Err(_) => Err(SecurityStateError::from("problem reading whitelist state")), - } - } - - pub fn is_data_fresh( - &self, - update_pref: &str, - allowed_staleness: &str, - ) -> Result { - let checked = match self.int_prefs.get(update_pref) { - Some(ch) => *ch, - None => 0, - }; - let staleness_seconds = match self.int_prefs.get(allowed_staleness) { - Some(st) => *st, - None => 0, - }; - - let update = SystemTime::UNIX_EPOCH + Duration::new(checked as u64, 0); - let staleness = Duration::new(staleness_seconds as u64, 0); - - Ok(match SystemTime::now().duration_since(update) { - Ok(duration) => duration <= staleness, - Err(_) => false, - }) - } - - pub fn is_blocklist_fresh(&self) -> Result { - self.is_data_fresh( - "services.blocklist.onecrl.checked", - "security.onecrl.maximum_staleness_in_seconds", - ) - } - - pub fn is_whitelist_fresh(&self) -> Result { - self.is_data_fresh( - "services.blocklist.intermediates.checked", - "security.onecrl.maximum_staleness_in_seconds", - ) - } - - pub fn is_enrollment_fresh(&self) -> Result { - self.is_data_fresh( - "services.blocklist.crlite.checked", - "security.onecrl.maximum_staleness_in_seconds", - ) - } - - pub fn pref_seen(&mut self, name: &str, value: i32) { - self.int_prefs.insert(name.to_owned(), value); - } -} - -fn get_path_from_directory_service(key: &str) -> Result { - let directory_service = match xpcom::services::get_DirectoryService() { - Some(ds) => ds, - _ => return Err(SecurityStateError::from("None")), - }; - - let cs_key = CString::new(key)?; - let mut requested_dir = GetterAddrefs::::new(); - - unsafe { - (*directory_service) - .Get( - (&cs_key).as_ptr(), - &nsIFile::IID as *const nsIID, - requested_dir.void_ptr(), - ) - .to_result() - .map_err(|res| SecurityStateError { - message: (*res.error_name()).as_str_unchecked().to_owned(), - }) - }?; - - let dir_path = match requested_dir.refptr() { - None => return Err(SecurityStateError::from("directory service failure")), - Some(refptr) => refptr, - }; - - let mut path = nsString::new(); - - unsafe { - (*dir_path) - .GetPath(&mut path as &mut nsAString) - // For reasons that aren't clear to me, NsresultExt does not - // implement std::error::Error (or Debug / Display). This map_err - // hack is a way to get an error with a useful message. - .to_result() - .map_err(|res| SecurityStateError { - message: (*res.error_name()).as_str_unchecked().to_owned(), - })?; - } - - Ok(PathBuf::from(format!("{}", path))) -} - -fn do_construct_cert_storage( - _outer: *const nsISupports, - iid: *const xpcom::nsIID, - result: *mut *mut xpcom::reexports::libc::c_void, -) -> Result<(), SecurityStateError> { - let path_buf = match get_path_from_directory_service("ProfD") { - Ok(path) => path, - Err(_) => match get_path_from_directory_service("TmpD") { - Ok(path) => path, - Err(e) => return Err(e), - }, - }; - - let cert_storage = CertStorage::allocate(InitCertStorage { - security_state: RwLock::new(SecurityState::new(path_buf)?), - }); - - unsafe { - cert_storage - .QueryInterface(iid, result) - // As above; greasy hack because NsresultExt - .to_result() - .map_err(|res| SecurityStateError { - message: (*res.error_name()).as_str_unchecked().to_owned(), - })?; - - return cert_storage.setup_prefs(); - }; -} - -fn read_int_pref(name: &str) -> Result { - let pref_service = match xpcom::services::get_PreferencesService() { - Some(ps) => ps, - _ => { - return Err(SecurityStateError::from( - "could not get preferences service", - )); - } - }; - - let prefs: RefPtr = match (*pref_service).query_interface() { - Some(pb) => pb, - _ => return Err(SecurityStateError::from("could not QI to nsIPrefBranch")), - }; - let pref_name = match CString::new(name) { - Ok(n) => n, - _ => return Err(SecurityStateError::from("could not build pref name string")), - }; - - let mut pref_value: i32 = -1; - - // We can't use GetIntPrefWithDefault because optional_argc is not - // supported. No matter, we can just check for failure and ignore - // any NS_ERROR_UNEXPECTED result. - let res = unsafe { (*prefs).GetIntPref((&pref_name).as_ptr(), (&mut pref_value) as *mut i32) }; - if !res.succeeded() { - match res.0 { - r if r == nsresult::NS_ERROR_UNEXPECTED as u32 => (), - _ => return Err(SecurityStateError::from("could not read pref")), - } - } - Ok(pref_value) -} - -#[no_mangle] -pub extern "C" fn cert_storage_constructor( - outer: *const nsISupports, - iid: *const xpcom::nsIID, - result: *mut *mut xpcom::reexports::libc::c_void, -) -> nserror::nsresult { - if !outer.is_null() { - return nserror::NS_ERROR_NO_AGGREGATION; - } - - match do_construct_cert_storage(outer, iid, result) { - Ok(_) => nserror::NS_OK, - Err(_) => { - // In future: log something so we know what went wrong? - nserror::NS_ERROR_FAILURE - } - } -} - -macro_rules! try_ns { - ($e:expr) => { - match $e { - Ok(value) => value, - Err(_) => return nserror::NS_ERROR_FAILURE, - } - }; -} - -#[derive(xpcom)] -#[xpimplements(nsICertStorage, nsIObserver)] -#[refcnt = "atomic"] -struct InitCertStorage { - security_state: RwLock, -} - -#[allow(non_snake_case)] -impl CertStorage { - unsafe fn setup_prefs(&self) -> Result<(), SecurityStateError> { - let int_prefs = [ - "services.blocklist.onecrl.checked", - "services.blocklist.intermediates.checked", - "services.blocklist.crlite.checked", - "security.onecrl.maximum_staleness_in_seconds", - ]; - - // Fetch add observers for relevant prefs - let pref_service = xpcom::services::get_PreferencesService().unwrap(); - let prefs: RefPtr = match (*pref_service).query_interface() { - Some(pb) => pb, - _ => return Err(SecurityStateError::from("could not QI to nsIPrefBranch")), - }; - - for pref in int_prefs.into_iter() { - let pref_nscstr = &nsCStr::from(pref.to_owned()) as &nsACString; - let rv = (*prefs).AddObserverImpl(pref_nscstr, self.coerce::(), false); - match read_int_pref(pref) { - Ok(up) => { - let mut ss = match self.security_state.write() { - Err(_) => return Err(SecurityStateError::from("could not get write lock")), - Ok(write_guard) => write_guard, - }; - ss.pref_seen(pref, up) - } - Err(_) => return Err(SecurityStateError::from("could not read pref")), - }; - assert!(rv.succeeded()); - } - - Ok(()) - } - - unsafe fn SetRevocationByIssuerAndSerial( - &self, - issuer: *const nsACString, - serial: *const nsACString, - state: i16, - ) -> nserror::nsresult { - if issuer.is_null() || serial.is_null() { - return nserror::NS_ERROR_FAILURE; - } - let issuer_decoded = try_ns!(base64::decode(&*issuer)); - let serial_decoded = try_ns!(base64::decode(&*serial)); - let mut ss = try_ns!(self.security_state.write()); - match ss.set_revocation_by_issuer_and_serial(&issuer_decoded, &serial_decoded, state) { - Ok(_) => nserror::NS_OK, - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn SetRevocationBySubjectAndPubKey( - &self, - subject: *const nsACString, - pub_key_base64: *const nsACString, - state: i16, - ) -> nserror::nsresult { - if subject.is_null() || pub_key_base64.is_null() { - return nserror::NS_ERROR_FAILURE; - } - let subject_decoded = try_ns!(base64::decode(&*subject)); - let pub_key_decoded = try_ns!(base64::decode(&*pub_key_base64)); - let mut ss = try_ns!(self.security_state.write()); - match ss.set_revocation_by_subject_and_pub_key(&subject_decoded, &pub_key_decoded, state) { - Ok(_) => nserror::NS_OK, - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn SetEnrollment( - &self, - issuer: *const nsACString, - serial: *const nsACString, - state: i16, - ) -> nserror::nsresult { - if issuer.is_null() || serial.is_null() { - return nserror::NS_ERROR_FAILURE; - } - let issuer_decoded = try_ns!(base64::decode(&*issuer)); - let serial_decoded = try_ns!(base64::decode(&*serial)); - let mut ss = try_ns!(self.security_state.write()); - match ss.set_enrollment(&issuer_decoded, &serial_decoded, state) { - Ok(_) => nserror::NS_OK, - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn SetWhitelist( - &self, - issuer: *const nsACString, - serial: *const nsACString, - state: i16, - ) -> nserror::nsresult { - if issuer.is_null() || serial.is_null() { - return nserror::NS_ERROR_FAILURE; - } - let issuer_decoded = try_ns!(base64::decode(&*issuer)); - let serial_decoded = try_ns!(base64::decode(&*serial)); - let mut ss = try_ns!(self.security_state.write()); - match ss.set_whitelist(&issuer_decoded, &serial_decoded, state) { - Ok(_) => nserror::NS_OK, - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn GetRevocationState( - &self, - issuer: *const nsACString, - serial: *const nsACString, - subject: *const nsACString, - pub_key_base64: *const nsACString, - state: *mut i16, - ) -> nserror::nsresult { - if issuer.is_null() || serial.is_null() || subject.is_null() || pub_key_base64.is_null() { - return nserror::NS_ERROR_FAILURE; - } - // TODO (bug 1535752): If we're calling this function when we already have binary data (e.g. - // in a TrustDomain::GetCertTrust callback), we should be able to pass in the binary data - // directly. See also bug 1535486. - let issuer_decoded = try_ns!(base64::decode(&*issuer)); - let serial_decoded = try_ns!(base64::decode(&*serial)); - let subject_decoded = try_ns!(base64::decode(&*subject)); - let pub_key_decoded = try_ns!(base64::decode(&*pub_key_base64)); - let ss = try_ns!(self.security_state.read()); - *state = nsICertStorage::STATE_UNSET as i16; - match ss.get_revocation_state( - &issuer_decoded, - &serial_decoded, - &subject_decoded, - &pub_key_decoded, - ) { - Ok(st) => { - *state = st; - nserror::NS_OK - } - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn GetEnrollmentState( - &self, - issuer: *const nsACString, - serial: *const nsACString, - state: *mut i16, - ) -> nserror::nsresult { - if issuer.is_null() || serial.is_null() { - return nserror::NS_ERROR_FAILURE; - } - let issuer_decoded = try_ns!(base64::decode(&*issuer)); - let serial_decoded = try_ns!(base64::decode(&*serial)); - let ss = try_ns!(self.security_state.read()); - *state = nsICertStorage::STATE_UNSET as i16; - match ss.get_enrollment_state(&issuer_decoded, &serial_decoded) { - Ok(st) => { - *state = st; - nserror::NS_OK - } - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn GetWhitelistState( - &self, - issuer: *const nsACString, - serial: *const nsACString, - state: *mut i16, - ) -> nserror::nsresult { - if issuer.is_null() || serial.is_null() { - return nserror::NS_ERROR_FAILURE; - } - let issuer_decoded = try_ns!(base64::decode(&*issuer)); - let serial_decoded = try_ns!(base64::decode(&*serial)); - let ss = try_ns!(self.security_state.read()); - *state = nsICertStorage::STATE_UNSET as i16; - match ss.get_whitelist_state(&issuer_decoded, &serial_decoded) { - Ok(st) => { - *state = st; - nserror::NS_OK - } - _ => nserror::NS_ERROR_FAILURE, - } - } - - unsafe fn IsBlocklistFresh(&self, fresh: *mut bool) -> nserror::nsresult { - *fresh = false; - let ss = try_ns!(self.security_state.read()); - *fresh = match ss.is_blocklist_fresh() { - Ok(is_fresh) => is_fresh, - Err(_) => false, - }; - - nserror::NS_OK - } - - unsafe fn IsWhitelistFresh(&self, fresh: *mut bool) -> nserror::nsresult { - *fresh = false; - let ss = try_ns!(self.security_state.read()); - *fresh = match ss.is_whitelist_fresh() { - Ok(is_fresh) => is_fresh, - Err(_) => false, - }; - - nserror::NS_OK - } - - unsafe fn IsEnrollmentFresh(&self, fresh: *mut bool) -> nserror::nsresult { - *fresh = false; - let ss = try_ns!(self.security_state.read()); - *fresh = match ss.is_enrollment_fresh() { - Ok(is_fresh) => is_fresh, - Err(_) => false, - }; - - nserror::NS_OK - } - - unsafe fn Observe( - &self, - subject: *const nsISupports, - topic: *const c_char, - pref_name: *const i16, - ) -> nserror::nsresult { - match CStr::from_ptr(topic).to_str() { - Ok("nsPref:changed") => { - let mut pref_value: i32 = 0; - - let prefs: RefPtr = match (*subject).query_interface() { - Some(pb) => pb, - _ => return nserror::NS_ERROR_FAILURE, - }; - - // Convert our wstring pref_name to a cstring (via nsCString's - // utf16 to utf8 conversion) - let mut len: usize = 0; - while (*(pref_name.offset(len as isize))) != 0 { - len += 1; - } - let name_slice = slice::from_raw_parts(pref_name as *const u16, len); - let mut name_string = nsCString::new(); - name_string.assign_utf16_to_utf8(name_slice); - - let pref_name = match CString::new(name_string.as_str_unchecked()) { - Ok(n) => n, - _ => return nserror::NS_ERROR_FAILURE, - }; - - let res = prefs.GetIntPref((&pref_name).as_ptr(), (&mut pref_value) as *mut i32); - - if !res.succeeded() { - return res; - } - - let mut ss = try_ns!(self.security_state.write()); - ss.pref_seen(name_string.as_str_unchecked(), pref_value); - } - _ => (), - } - nserror::NS_OK - } -} diff --git a/security/manager/ssl/components.conf b/security/manager/ssl/components.conf index 35186a381bcd..6701e4512b80 100644 --- a/security/manager/ssl/components.conf +++ b/security/manager/ssl/components.conf @@ -157,10 +157,10 @@ Classes = [ 'legacy_constructor': 'mozilla::psm::NSSConstructor', }, { - 'cid': '{16e5c837-f877-4e23-9c64-eddf905e30e6}', - 'contract_ids': ['@mozilla.org/security/certstorage;1'], - 'headers': ['/security/manager/ssl/cert_storage/src/cert_storage.h'], - 'legacy_constructor': 'construct_cert_storage', + 'cid': '{11aefd53-2fbb-4c92-a0c1-053212ae42d0}', + 'contract_ids': ['@mozilla.org/security/certblocklist;1'], + 'type': 'CertBlocklist', + 'legacy_constructor': 'mozilla::psm::NSSConstructor', }, { 'cid': '{57972956-5718-42d2-8070-b3fc72212eaf}', diff --git a/security/manager/ssl/moz.build b/security/manager/ssl/moz.build index 152183e552c2..cba9b9c13a02 100644 --- a/security/manager/ssl/moz.build +++ b/security/manager/ssl/moz.build @@ -11,9 +11,9 @@ XPIDL_SOURCES += [ 'nsIASN1PrintableItem.idl', 'nsIASN1Sequence.idl', 'nsIBadCertListener2.idl', + 'nsICertBlocklist.idl', 'nsICertificateDialogs.idl', 'nsICertOverrideService.idl', - 'nsICertStorage.idl', 'nsIClientAuthDialogs.idl', 'nsIContentSignatureVerifier.idl', 'nsICryptoHash.idl', @@ -99,7 +99,7 @@ EXPORTS.ipc += [ ] UNIFIED_SOURCES += [ - 'cert_storage/src/cert_storage.cpp', + 'CertBlocklist.cpp', 'ContentSignatureVerifier.cpp', 'CryptoTask.cpp', 'CSTrustDomain.cpp', diff --git a/security/manager/ssl/nsICertBlocklist.idl b/security/manager/ssl/nsICertBlocklist.idl new file mode 100644 index 000000000000..9d8f4eb2a3dd --- /dev/null +++ b/security/manager/ssl/nsICertBlocklist.idl @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIX509Cert; + +%{C++ +#define NS_CERTBLOCKLIST_CONTRACTID "@mozilla.org/security/certblocklist;1" +%} + +/** + * Represents a service to add certificates as explicitly blocked/distrusted. + */ +[scriptable, uuid(e0654480-f433-11e4-b939-0800200c9a66)] +interface nsICertBlocklist : nsISupports { + /** + * Add details of a revoked certificate : + * issuer name (base-64 encoded DER) and serial number (base-64 encoded DER). + */ + [must_use] + void revokeCertByIssuerAndSerial(in ACString issuer, + in ACString serialNumber); + + /** + * Add details of a revoked certificate : + * subject name (base-64 encoded DER) and hash of public key (base-64 encoded + * sha-256 hash of the public key). + */ + [must_use] + void revokeCertBySubjectAndPubKey(in ACString subject, + in ACString pubKeyHash); + + /** + * Persist (fresh) blocklist entries to the profile (if a profile directory is + * available). Note: calling this will result in synchronous I/O. + */ + [must_use] + void saveEntries(); + + /** + * Check if a certificate is blocked. + * issuer - issuer name, DER, Base64 encoded + * serial - serial number, DER, BAse64 encoded + * subject - subject name, DER, Base64 encoded + * pubkey - public key, DER, Base64 encoded + */ + [must_use] + boolean isCertRevoked(in ACString issuer, + in ACString serial, + in ACString subject, + in ACString pubkey); + + /** + * Check that the blocklist data is current. Specifically, that the current + * time is no more than security.onecrl.maximum_staleness_in_seconds seconds + * after the last blocklist update (as stored in the + * app.update.lastUpdateTime.blocklist-background-update-timer pref) + */ + [must_use] + boolean isBlocklistFresh(); +}; diff --git a/security/manager/ssl/nsICertStorage.idl b/security/manager/ssl/nsICertStorage.idl deleted file mode 100644 index 740c403c0a3b..000000000000 --- a/security/manager/ssl/nsICertStorage.idl +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -%{C++ -#define NS_CERTSTORAGE_CONTRACTID "@mozilla.org/security/certstorage;1" -%} - -[scriptable, uuid(327100a7-3401-45ef-b160-bf880f1016fd)] -interface nsICertStorage : nsISupports { - const short STATE_UNSET = 0; - const short STATE_ENFORCE = 1; - - /** - * Set the revocation state of a certificate by issuer and serial number: - * issuer name (base-64 encoded DER) and serial number (base-64 encoded DER). - */ - [must_use] - void setRevocationByIssuerAndSerial(in ACString issuer, - in ACString serialNumber, - in short state); - - /** - * Set the revocation state of a certificate by subject and public key hash: - * subject name (base-64 encoded DER) and hash of public key (base-64 encoded - * sha-256 hash of the public key). - * state (short) is STATE_ENFORCE for revoked certs, STATE_UNSET otherwise. - */ - [must_use] - void setRevocationBySubjectAndPubKey(in ACString subject, - in ACString pubKeyHash, - in short state); - - /** - * Set the whitelist state of an intermediate certificate by issuer and - * serial number: - * issuer name (base-64 encoded DER) and serial number (base-64 encoded DER). - * state (short) is STATE_ENFORCE for whitelisted certs, STATE_UNSET otherwise. - */ - [must_use] - void setWhitelist(in ACString issuer, - in ACString serialNumber, - in short state); - - /** - * Set the CRLite enrollment state of a certificate by issuer and serial - * number: - * issuer name (base-64 encoded DER) and serial number (base-64 encoded DER). - * state (short) is STATE_ENFORCE for enrolled certs, STATE_UNSET otherwise. - */ - [must_use] - void setEnrollment(in ACString issuer, - in ACString serialNumber, - in short state); - - /** - * Get the revocation state of a certificate. - * issuer - issuer name, DER, Base64 encoded - * serial - serial number, DER, Base64 encoded - * subject - subject name, DER, Base64 encoded - * pubkey - public key, DER, Base64 encoded - */ - [must_use] - short getRevocationState(in ACString issuer, - in ACString serial, - in ACString subject, - in ACString pubkey); - - /** - * Get the CRLite enrollment status of a certificate. - * issuer - issuer name, DER, Base64 encoded - * serial - serial number, DER, Base64 encoded - */ - [must_use] - short getEnrollmentState(in ACString issuer, - in ACString serial); - - /** - * Get the whitelist status of an intermediate certificate. - * issuer - issuer name, DER, Base64 encoded - * serial - serial number, DER, Base64 encoded - */ - [must_use] - short getWhitelistState(in ACString issuer, - in ACString serial); - - /** - * Check that the blocklist data is current. Specifically, that the current - * time is no more than security.onecrl.maximum_staleness_in_seconds seconds - * after the last blocklist update (as stored in the - * services.blocklist.onecrl.checked pref) - */ - [must_use] - boolean isBlocklistFresh(); - - /** - * Check that the whitelist data is current. Specifically, that the current - * time is no more than security.onecrl.maximum_staleness_in_seconds seconds - * after the last whitelist update (as stored in the - * services.blocklist.intermediates.checked pref) - */ - [must_use] - boolean isWhitelistFresh(); - - /** - * Check that the CRLite enrollment data is current. Specifically, that the current - * time is no more than security.onecrl.maximum_staleness_in_seconds seconds - * after the last crlite enrollment update (as stored in the - * services.blocklist.crlite.checked pref) - */ - [must_use] - boolean isEnrollmentFresh(); -}; diff --git a/security/manager/ssl/nsNSSModule.cpp b/security/manager/ssl/nsNSSModule.cpp index 0ed630de230e..c7ee273c9809 100644 --- a/security/manager/ssl/nsNSSModule.cpp +++ b/security/manager/ssl/nsNSSModule.cpp @@ -6,10 +6,9 @@ #include "nsNSSModule.h" +#include "CertBlocklist.h" #include "ContentSignatureVerifier.h" #include "NSSErrorsService.h" -#include "OSKeyStore.h" -#include "OSReauthenticator.h" #include "PKCS11ModuleDB.h" #include "PSMContentListener.h" #include "SecretDecoderRing.h" @@ -34,6 +33,8 @@ #include "nsSecureBrowserUIImpl.h" #include "nsSiteSecurityService.h" #include "nsXULAppAPI.h" +#include "OSKeyStore.h" +#include "OSReauthenticator.h" #ifdef MOZ_XUL # include "nsCertTree.h" @@ -147,6 +148,8 @@ IMPL(nsRandomGenerator, nullptr, ProcessRestriction::AnyProcess) IMPL(TransportSecurityInfo, nullptr, ProcessRestriction::AnyProcess) IMPL(nsSiteSecurityService, &nsSiteSecurityService::Init, ProcessRestriction::AnyProcess, ThreadRestriction::MainThreadOnly) +IMPL(CertBlocklist, &CertBlocklist::Init, ProcessRestriction::ParentProcessOnly, + ThreadRestriction::MainThreadOnly) IMPL(OSKeyStore, nullptr, ProcessRestriction::ParentProcessOnly, ThreadRestriction::MainThreadOnly) IMPL(OSReauthenticator, nullptr, ProcessRestriction::ParentProcessOnly, diff --git a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js index 04673992a02b..3c151d4ac98e 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js @@ -100,12 +100,11 @@ add_task(async function testRevoked() { // Note that there's currently no way to un-do this. This should only be a // problem if another test re-uses a certificate with this same key (perhaps // likely) and subject (less likely). - let certBlocklist = Cc["@mozilla.org/security/certstorage;1"] - .getService(Ci.nsICertStorage); - certBlocklist.setRevocationBySubjectAndPubKey( + let certBlocklist = Cc["@mozilla.org/security/certblocklist;1"] + .getService(Ci.nsICertBlocklist); + certBlocklist.revokeCertBySubjectAndPubKey( "MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked - "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", // hash of the shared key - Ci.nsICertStorage.STATE_ENFORCE); // yes, we want this to be revoked + "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8="); // hash of the shared key let cert = await readCertificate("revoked.pem", ",,"); let win = await displayCertificate(cert); // As of bug 1312827, OneCRL only applies to TLS web server certificates, so diff --git a/security/manager/ssl/tests/unit/test_cert_storage.js b/security/manager/ssl/tests/unit/test_cert_blocklist.js similarity index 73% rename from security/manager/ssl/tests/unit/test_cert_storage.js rename to security/manager/ssl/tests/unit/test_cert_blocklist.js index e9c0a90ea7b5..588ba7afe733 100644 --- a/security/manager/ssl/tests/unit/test_cert_storage.js +++ b/security/manager/ssl/tests/unit/test_cert_blocklist.js @@ -12,9 +12,9 @@ // * it does a sanity check to ensure other cert verifier behavior is // unmodified -const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm", {}); -const { RemoteSettings } = ChromeUtils.import("resource://services-settings/remote-settings.js", {}); -const BlocklistClients = ChromeUtils.import("resource://services-common/blocklist-clients.js", {}); +const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm"); +const { RemoteSettings } = ChromeUtils.import("resource://services-settings/remote-settings.js"); +const BlocklistClients = ChromeUtils.import("resource://services-common/blocklist-clients.js", null); // First, we need to setup appInfo for the blocklist service to work var id = "xpcshell@tests.mozilla.org"; @@ -187,8 +187,8 @@ function load_cert(cert, trust) { function test_is_revoked(certList, issuerString, serialString, subjectString, pubKeyString) { - return certList.getRevocationState(btoa(issuerString), btoa(serialString), - btoa(subjectString), btoa(pubKeyString)) == Ci.nsICertStorage.STATE_ENFORCE; + return certList.isCertRevoked(btoa(issuerString), btoa(serialString), + btoa(subjectString), btoa(pubKeyString)); } function fetch_blocklist() { @@ -202,14 +202,89 @@ function fetch_blocklist() { return RemoteSettings.pollChanges(); } +function* generate_revocations_txt_lines() { + let profile = do_get_profile(); + let revocations = profile.clone(); + revocations.append("revocations.txt"); + ok(revocations.exists(), "the revocations file should exist"); + let inputStream = Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Ci.nsIFileInputStream); + inputStream.init(revocations, -1, -1, 0); + inputStream.QueryInterface(Ci.nsILineInputStream); + let hasmore = false; + do { + let line = {}; + hasmore = inputStream.readLine(line); + yield line.value; + } while (hasmore); +} + +// Check that revocations.txt contains, in any order, the lines +// ("top-level lines") that are the keys in |expected|, each followed +// immediately by the lines ("sublines") in expected[topLevelLine] +// (again, in any order). +function check_revocations_txt_contents(expected) { + let lineGenerator = generate_revocations_txt_lines(); + let firstLine = lineGenerator.next(); + equal(firstLine.done, false, + "first line of revocations.txt should be present"); + equal(firstLine.value, "# Auto generated contents. Do not edit.", + "first line of revocations.txt"); + let line = lineGenerator.next(); + let topLevelFound = {}; + while (true) { + if (line.done) { + break; + } + + ok(line.value in expected, + `${line.value} should be an expected top-level line in revocations.txt`); + ok(!(line.value in topLevelFound), + `should not have seen ${line.value} before in revocations.txt`); + topLevelFound[line.value] = true; + let topLevelLine = line.value; + + let sublines = expected[line.value]; + let subFound = {}; + while (true) { + line = lineGenerator.next(); + if (line.done || !(line.value in sublines)) { + break; + } + ok(!(line.value in subFound), + `should not have seen ${line.value} before in revocations.txt`); + subFound[line.value] = true; + } + for (let subline in sublines) { + ok(subFound[subline], + `should have found ${subline} below ${topLevelLine} in revocations.txt`); + } + } + for (let topLevelLine in expected) { + ok(topLevelFound[topLevelLine], + `should have found ${topLevelLine} in revocations.txt`); + } +} + function run_test() { // import the certificates we need load_cert("test-ca", "CTu,CTu,CTu"); load_cert("test-int", ",,"); load_cert("other-test-ca", "CTu,CTu,CTu"); - let certList = Cc["@mozilla.org/security/certstorage;1"] - .getService(Ci.nsICertStorage); + let certList = Cc["@mozilla.org/security/certblocklist;1"] + .getService(Ci.nsICertBlocklist); + + let expected = { "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5": + { "\tVCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=": true }, + "MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=": + { " Rym6o+VN9xgZXT/QLrvN/nv1ZN4=": true}, + "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=": + { " a0X7/7DlTaedpgrIJg25iBPOkIM=": true}, + "YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy": + { " YW5vdGhlciBzZXJpYWwu": true, + " c2VyaWFsMi4=": true }, + }; add_task(async function() { // check some existing items in revocations.txt are blocked. Since the @@ -273,6 +348,10 @@ function run_test() { "some imaginary subject", "some imaginary pubkey"), "issuer / serial pair should be blocked"); + // Check the blocklist entry has been persisted properly to the backing + // file + check_revocations_txt_contents(expected); + // Check the blocklisted intermediate now causes a failure let file = "test_onecrl/test-int-ee.pem"; await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE); @@ -295,10 +374,22 @@ function run_test() { // Check a bad cert is still bad (unknown issuer) file = "bad_certs/unknownissuer.pem"; await verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER); + + // check that save with no further update is a no-op + let lastModified = gRevocations.lastModifiedTime; + // add an already existing entry + certList.revokeCertByIssuerAndSerial("YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy", + "c2VyaWFsMi4="); + certList.saveEntries(); + let newModified = gRevocations.lastModifiedTime; + equal(lastModified, newModified, + "saveEntries with no modifications should not update the backing file"); }); - add_task(async function() { - ok(certList.isBlocklistFresh(), "Blocklist should be fresh."); + add_test(function() { + // Check the blocklist entry has not changed + check_revocations_txt_contents(expected); + run_next_test(); }); run_next_test(); diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index ebad1a912702..9f4cf04fd806 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -46,7 +46,7 @@ support-files = # exist on that platform. # FIPS still works on Linux/Windows, so this test doesn't make any sense there. skip-if = os != 'mac' -[test_cert_storage.js] +[test_cert_blocklist.js] tags = addons psm blocklist [test_cert_chains.js] run-sequentially = hardcoded ports diff --git a/services/common/blocklist-clients.js b/services/common/blocklist-clients.js index f0b20db27de8..0cd8a9c714e8 100644 --- a/services/common/blocklist-clients.js +++ b/services/common/blocklist-clients.js @@ -39,33 +39,17 @@ const PREF_BLOCKLIST_GFX_SIGNER = "services.blocklist.gfx.signer"; * * @param {Object} data Current records in the local db. */ -async function updateCertBlocklist({ data: { created, updated, deleted } }) { - const certList = Cc["@mozilla.org/security/certstorage;1"] - .getService(Ci.nsICertStorage); - for (let item of deleted) { - if (item.issuerName && item.serialNumber) { - certList.setRevocationByIssuerAndSerial(item.issuerName, - item.serialNumber, - Ci.nsICertStorage.STATE_UNSET); - } else if (item.subject && item.pubKeyHash) { - certList.setRevocationBySubjectAndPubKey(item.subject, - item.pubKeyHash, - Ci.nsICertStorage.STATE_UNSET); - } - } - - const toAdd = created.concat(updated.map(u => u.new)); - - for (let item of toAdd) { +async function updateCertBlocklist({ data: { current: records } }) { + const certList = Cc["@mozilla.org/security/certblocklist;1"] + .getService(Ci.nsICertBlocklist); + for (let item of records) { try { if (item.issuerName && item.serialNumber) { - certList.setRevocationByIssuerAndSerial(item.issuerName, - item.serialNumber, - Ci.nsICertStorage.STATE_ENFORCE); + certList.revokeCertByIssuerAndSerial(item.issuerName, + item.serialNumber); } else if (item.subject && item.pubKeyHash) { - certList.setRevocationBySubjectAndPubKey(item.subject, - item.pubKeyHash, - Ci.nsICertStorage.STATE_ENFORCE); + certList.revokeCertBySubjectAndPubKey(item.subject, + item.pubKeyHash); } } catch (e) { // prevent errors relating to individual blocklist entries from @@ -74,6 +58,7 @@ async function updateCertBlocklist({ data: { created, updated, deleted } }) { Cu.reportError(e); } } + certList.saveEntries(); } /** diff --git a/toolkit/library/rust/shared/Cargo.toml b/toolkit/library/rust/shared/Cargo.toml index 41e8f792e1c0..777e65bc107c 100644 --- a/toolkit/library/rust/shared/Cargo.toml +++ b/toolkit/library/rust/shared/Cargo.toml @@ -32,7 +32,6 @@ env_logger = {version = "0.5", default-features = false} # disable `regex` to re cose-c = { version = "0.1.5" } jsrust_shared = { path = "../../../../js/src/rust/shared", optional = true } arrayvec = "0.4" -cert_storage = { path = "../../../../security/manager/ssl/cert_storage" } [build-dependencies] rustc_version = "0.2" diff --git a/toolkit/library/rust/shared/lib.rs b/toolkit/library/rust/shared/lib.rs index f5131e1bfad8..579a4af36f38 100644 --- a/toolkit/library/rust/shared/lib.rs +++ b/toolkit/library/rust/shared/lib.rs @@ -31,7 +31,6 @@ extern crate env_logger; extern crate u2fhid; extern crate gkrust_utils; extern crate log; -extern crate cert_storage; extern crate cosec; extern crate rsdparsa_capi; #[cfg(feature = "spidermonkey_rust")] diff --git a/xpcom/build/Services.py b/xpcom/build/Services.py index 38ed022ad2b1..81bdef7dc053 100644 --- a/xpcom/build/Services.py +++ b/xpcom/build/Services.py @@ -14,8 +14,6 @@ service('ToolkitChromeRegistryService', 'nsIToolkitChromeRegistry', "@mozilla.org/chrome/chrome-registry;1") service('XULChromeRegistryService', 'nsIXULChromeRegistry', "@mozilla.org/chrome/chrome-registry;1") -service('DirectoryService', 'nsIProperties', - "@mozilla.org/file/directory_service;1"), service('IOService', 'nsIIOService', "@mozilla.org/network/io-service;1") service('ObserverService', 'nsIObserverService', @@ -24,8 +22,6 @@ service('StringBundleService', 'nsIStringBundleService', "@mozilla.org/intl/stringbundle;1") service('PermissionManager', 'nsIPermissionManager', "@mozilla.org/permissionmanager;1") -service('PreferencesService', 'nsIPrefService', - "@mozilla.org/preferences-service;1") service('ServiceWorkerManager', 'nsIServiceWorkerManager', "@mozilla.org/serviceworkers/manager;1") service('AsyncShutdown', 'nsIAsyncShutdownService', @@ -71,7 +67,6 @@ CPP_INCLUDES = """ #include "IHistory.h" #include "nsIXPConnect.h" #include "nsIPermissionManager.h" -#include "nsIPrefService.h" #include "nsIServiceWorkerManager.h" #include "nsICacheStorageService.h" #include "nsIStreamTransportService.h"