mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1547877 - enable configuration of new cert storage implementation r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D29306 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
926c25f9a4
commit
bfe7c7e0b4
@ -81,7 +81,11 @@
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsIRedirectHistoryEntry.h"
|
||||
#include "nsICertStorage.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsICertStorage.h"
|
||||
#else
|
||||
# include "nsICertBlocklist.h"
|
||||
#endif
|
||||
#include "nsICertOverrideService.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "mozIThirdPartyUtil.h"
|
||||
@ -2565,7 +2569,11 @@ void net_EnsurePSMInit() {
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsISupports> sss = do_GetService(NS_SSSERVICE_CONTRACTID);
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsCOMPtr<nsISupports> cbl = do_GetService(NS_CERTSTORAGE_CONTRACTID);
|
||||
#else
|
||||
nsCOMPtr<nsISupports> cbl = do_GetService(NS_CERTBLOCKLIST_CONTRACTID);
|
||||
#endif
|
||||
nsCOMPtr<nsISupports> cos = do_GetService(NS_CERTOVERRIDE_CONTRACTID);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,9 @@
|
||||
#include "PublicKeyPinningService.h"
|
||||
#include "cert.h"
|
||||
#include "certdb.h"
|
||||
#include "cert_storage/src/cert_storage.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "cert_storage/src/cert_storage.h"
|
||||
#endif
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Move.h"
|
||||
@ -88,7 +90,11 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
|
||||
mBuiltChain(builtChain),
|
||||
mPinningTelemetryInfo(pinningTelemetryInfo),
|
||||
mHostname(hostname),
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
|
||||
#else
|
||||
mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)),
|
||||
#endif
|
||||
mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED),
|
||||
mSCTListFromCertificate(),
|
||||
mSCTListFromOCSPStapling() {}
|
||||
@ -98,6 +104,7 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
|
||||
Vector<Input> rootCandidates;
|
||||
Vector<Input> intermediateCandidates;
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
if (!mCertStorage) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
@ -122,6 +129,7 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
|
||||
return Result::FATAL_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
|
||||
|
||||
@ -216,13 +224,18 @@ Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
}
|
||||
|
||||
// Check the certificate against the OneCRL cert blocklist
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
if (!mCertStorage) {
|
||||
#else
|
||||
if (!mCertBlocklist) {
|
||||
#endif
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
// The certificate blocklist currently only applies to TLS server
|
||||
// certificates.
|
||||
if (mCertDBTrustType == trustSSL) {
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
int16_t revocationState;
|
||||
|
||||
nsTArray<uint8_t> issuerBytes;
|
||||
@ -232,18 +245,38 @@ Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
|
||||
nsresult nsrv = BuildRevocationCheckArrays(
|
||||
candidateCert, issuerBytes, serialBytes, subjectBytes, pubKeyBytes);
|
||||
#else
|
||||
bool isCertRevoked;
|
||||
|
||||
nsAutoCString encIssuer;
|
||||
nsAutoCString encSerial;
|
||||
nsAutoCString encSubject;
|
||||
nsAutoCString encPubKey;
|
||||
|
||||
nsresult nsrv = BuildRevocationCheckStrings(
|
||||
candidateCert.get(), encIssuer, encSerial, encSubject, encPubKey);
|
||||
#endif
|
||||
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsrv = mCertStorage->GetRevocationState(
|
||||
issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
|
||||
#else
|
||||
nsrv = mCertBlocklist->IsCertRevoked(encIssuer, encSerial, encSubject,
|
||||
encPubKey, &isCertRevoked);
|
||||
#endif
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
if (revocationState == nsICertStorage::STATE_ENFORCE) {
|
||||
#else
|
||||
if (isCertRevoked) {
|
||||
#endif
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("NSSCertDBTrustDomain: certificate is in blocklist"));
|
||||
return Result::ERROR_REVOKED_CERTIFICATE;
|
||||
@ -497,7 +530,11 @@ Result NSSCertDBTrustDomain::CheckRevocation(
|
||||
|
||||
// If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
|
||||
bool blocklistIsFresh;
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsresult nsrv = mCertStorage->IsBlocklistFresh(&blocklistIsFresh);
|
||||
#else
|
||||
nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
|
||||
#endif
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
@ -1273,6 +1310,7 @@ nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsresult BuildRevocationCheckArrays(const UniqueCERTCertificate& cert,
|
||||
/*out*/ nsTArray<uint8_t>& issuerBytes,
|
||||
/*out*/ nsTArray<uint8_t>& serialBytes,
|
||||
@ -1304,6 +1342,45 @@ nsresult BuildRevocationCheckArrays(const UniqueCERTCertificate& cert,
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
#else
|
||||
nsresult BuildRevocationCheckStrings(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& encIssuer,
|
||||
/*out*/ nsCString& encSerial,
|
||||
/*out*/ nsCString& encSubject,
|
||||
/*out*/ nsCString& encPubKey) {
|
||||
// Convert issuer, serial, subject and pubKey data to Base64 encoded DER
|
||||
nsDependentCSubstring issuerString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->derIssuer.data), cert->derIssuer.len);
|
||||
nsDependentCSubstring serialString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->serialNumber.data),
|
||||
cert->serialNumber.len);
|
||||
nsDependentCSubstring subjectString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->derSubject.data),
|
||||
cert->derSubject.len);
|
||||
nsDependentCSubstring pubKeyString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->derPublicKey.data),
|
||||
cert->derPublicKey.len);
|
||||
|
||||
nsresult rv = Base64Encode(issuerString, encIssuer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(serialString, encSerial);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(subjectString, encSubject);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(pubKeyString, encPubKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Given a list of certificates representing a verified certificate path from an
|
||||
|
@ -11,7 +11,11 @@
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsICertStorage.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsICertStorage.h"
|
||||
#else
|
||||
# include "nsICertBlocklist.h"
|
||||
#endif
|
||||
#include "nsString.h"
|
||||
#include "mozpkix/pkixtypes.h"
|
||||
#include "secmodt.h"
|
||||
@ -59,6 +63,7 @@ void UnloadLoadableRoots();
|
||||
nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& nickname);
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
/**
|
||||
* Build nsTArray<uint8_t>s out of the issuer, serial, subject and public key
|
||||
* data from the supplied certificate for use in revocation checks.
|
||||
@ -82,6 +87,31 @@ nsresult BuildRevocationCheckArrays(const UniqueCERTCertificate& cert,
|
||||
/*out*/ nsTArray<uint8_t>& serialBytes,
|
||||
/*out*/ nsTArray<uint8_t>& subjectBytes,
|
||||
/*out*/ nsTArray<uint8_t>& pubKeyBytes);
|
||||
#else
|
||||
/**
|
||||
* Build strings of base64 encoded issuer, serial, subject and public key data
|
||||
* from the supplied certificate for use in revocation checks.
|
||||
*
|
||||
* @param cert
|
||||
* The CERTCertificate* from which to extract the data.
|
||||
* @param out encIssuer
|
||||
* The string to populate with base64 encoded issuer data.
|
||||
* @param out encSerial
|
||||
* The string to populate with base64 encoded serial number data.
|
||||
* @param out encSubject
|
||||
* The string to populate with base64 encoded subject data.
|
||||
* @param out encPubKey
|
||||
* The string to populate with base64 encoded public key data.
|
||||
* @return
|
||||
* NS_OK, unless there's a Base64 encoding problem, in which case
|
||||
* NS_ERROR_FAILURE.
|
||||
*/
|
||||
nsresult BuildRevocationCheckStrings(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& encIssuer,
|
||||
/*out*/ nsCString& encSerial,
|
||||
/*out*/ nsCString& encSubject,
|
||||
/*out*/ nsCString& encPubKey);
|
||||
#endif
|
||||
|
||||
void SaveIntermediateCerts(const UniqueCERTCertList& certList);
|
||||
|
||||
@ -232,7 +262,11 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
|
||||
UniqueCERTCertList& mBuiltChain; // non-owning
|
||||
PinningTelemetryInfo* mPinningTelemetryInfo;
|
||||
const char* mHostname; // non-owning - only used for pinning checks
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsCOMPtr<nsICertStorage> mCertStorage;
|
||||
#else
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
#endif
|
||||
CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
|
||||
// Certificate Transparency data extracted during certificate verification
|
||||
UniqueSECItem mSCTListFromCertificate;
|
||||
|
@ -4,11 +4,15 @@
|
||||
* 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"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "cert_storage/src/cert_storage.h"
|
||||
#endif
|
||||
#include "CSTrustDomain.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsDirectoryServiceUtils.h"
|
||||
#endif
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
@ -26,7 +30,11 @@ static LazyLogModule gTrustDomainPRLog("CSTrustDomain");
|
||||
|
||||
CSTrustDomain::CSTrustDomain(UniqueCERTCertList& certChain)
|
||||
: mCertChain(certChain),
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
mCertBlocklist(do_GetService(NS_CERT_STORAGE_CID)) {}
|
||||
#else
|
||||
mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) {}
|
||||
#endif
|
||||
|
||||
Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
const CertPolicyId& policy,
|
||||
@ -44,6 +52,7 @@ Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsTArray<uint8_t> issuerBytes;
|
||||
nsTArray<uint8_t> serialBytes;
|
||||
nsTArray<uint8_t> subjectBytes;
|
||||
@ -51,18 +60,37 @@ Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
||||
|
||||
nsresult nsrv = BuildRevocationCheckArrays(
|
||||
candidateCert, issuerBytes, serialBytes, subjectBytes, pubKeyBytes);
|
||||
#else
|
||||
nsAutoCString encIssuer;
|
||||
nsAutoCString encSerial;
|
||||
nsAutoCString encSubject;
|
||||
nsAutoCString encPubKey;
|
||||
|
||||
nsresult nsrv = BuildRevocationCheckStrings(candidateCert.get(), encIssuer,
|
||||
encSerial, encSubject, encPubKey);
|
||||
#endif
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
int16_t revocationState;
|
||||
nsrv = mCertBlocklist->GetRevocationState(
|
||||
issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
|
||||
#else
|
||||
bool isCertRevoked;
|
||||
nsrv = mCertBlocklist->IsCertRevoked(encIssuer, encSerial, encSubject,
|
||||
encPubKey, &isCertRevoked);
|
||||
#endif
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
if (revocationState == nsICertStorage::STATE_ENFORCE) {
|
||||
#else
|
||||
if (isCertRevoked) {
|
||||
#endif
|
||||
CSTrust_LOG(("CSTrustDomain: certificate is revoked\n"));
|
||||
return Result::ERROR_REVOKED_CERTIFICATE;
|
||||
}
|
||||
|
@ -11,7 +11,11 @@
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsICertStorage.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsICertStorage.h"
|
||||
#else
|
||||
# include "nsICertBlocklist.h"
|
||||
#endif
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
@ -73,7 +77,11 @@ class CSTrustDomain final : public mozilla::pkix::TrustDomain {
|
||||
|
||||
private:
|
||||
/*out*/ UniqueCERTCertList& mCertChain;
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
nsCOMPtr<nsICertStorage> mCertBlocklist;
|
||||
#else
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
|
627
security/manager/ssl/CertBlocklist.cpp
Normal file
627
security/manager/ssl/CertBlocklist.cpp
Normal file
@ -0,0 +1,627 @@
|
||||
/* -*- 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.settings.security.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<char*, uint8_t*>(mDNData),
|
||||
mDNLength);
|
||||
nsDependentCSubstring otherString(BitwiseCast<char*, uint8_t*>(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<nsIFileInputStream> 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<nsILineInputStream> 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<const uint8_t*, const char*>(decodedDN.get()),
|
||||
decodedDN.Length(),
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedOther.get()),
|
||||
decodedOther.Length(), aMechanism);
|
||||
|
||||
if (aItemState == CertNewFromBlocklist) {
|
||||
// We want SaveEntries to be a no-op if no new entries are added.
|
||||
nsGenericHashKey<CertBlocklistItem>* 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<nsIOutputStream> 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<BlocklistStringSet> 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<nsISafeOutputStream> 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<const uint8_t*, const char*>(decodedIssuer.get()),
|
||||
decodedIssuer.Length(),
|
||||
BitwiseCast<const uint8_t*, const char*>(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<nsICryptoHash> crypto;
|
||||
crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
|
||||
rv = crypto->Init(nsICryptoHash::SHA256);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = crypto->Update(
|
||||
BitwiseCast<const uint8_t*, const char*>(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<const uint8_t*, const char*>(decodedSubject.get()),
|
||||
decodedSubject.Length(),
|
||||
BitwiseCast<const uint8_t*, const char*>(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));
|
||||
}
|
||||
}
|
89
security/manager/ssl/CertBlocklist.h
Normal file
89
security/manager/ssl/CertBlocklist.h
Normal file
@ -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<CertBlocklistItem> BlocklistItemKey;
|
||||
typedef nsTHashtable<BlocklistItemKey> BlocklistTable;
|
||||
typedef nsTHashtable<nsCStringHashKey> BlocklistStringSet;
|
||||
typedef nsClassHashtable<nsCStringHashKey, BlocklistStringSet> 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<nsIFile> 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
|
@ -18,7 +18,9 @@
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsIFileStreams.h"
|
||||
#endif
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsITimer.h"
|
||||
|
@ -8,8 +8,10 @@
|
||||
#include "nsINSSErrorsService.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsISafeOutputStream.h"
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
# include "nsILineInputStream.h"
|
||||
# include "nsISafeOutputStream.h"
|
||||
#endif
|
||||
#include "nsIStringBundle.h"
|
||||
#include "prerror.h"
|
||||
|
||||
|
@ -156,12 +156,6 @@ Classes = [
|
||||
'type': 'nsSiteSecurityService',
|
||||
'legacy_constructor': 'mozilla::psm::NSSConstructor<nsSiteSecurityService>',
|
||||
},
|
||||
{
|
||||
'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': '{57972956-5718-42d2-8070-b3fc72212eaf}',
|
||||
'contract_ids': ['@mozilla.org/security/oskeystore;1'],
|
||||
@ -185,3 +179,22 @@ if defined('MOZ_XUL'):
|
||||
'legacy_constructor': 'mozilla::psm::NSSConstructor<nsCertTree>',
|
||||
},
|
||||
]
|
||||
|
||||
if defined('MOZ_NEW_CERT_STORAGE'):
|
||||
Classes += [
|
||||
{
|
||||
'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',
|
||||
},
|
||||
]
|
||||
else:
|
||||
Classes += [
|
||||
{
|
||||
'cid': '{11aefd53-2fbb-4c92-a0c1-053212ae42d0}',
|
||||
'contract_ids': ['@mozilla.org/security/certblocklist;1'],
|
||||
'type': 'CertBlocklist',
|
||||
'legacy_constructor': 'mozilla::psm::NSSConstructor<CertBlocklist>',
|
||||
},
|
||||
]
|
||||
|
@ -13,7 +13,6 @@ XPIDL_SOURCES += [
|
||||
'nsIBadCertListener2.idl',
|
||||
'nsICertificateDialogs.idl',
|
||||
'nsICertOverrideService.idl',
|
||||
'nsICertStorage.idl',
|
||||
'nsIClientAuthDialogs.idl',
|
||||
'nsIContentSignatureVerifier.idl',
|
||||
'nsICryptoHash.idl',
|
||||
@ -94,7 +93,6 @@ EXPORTS.ipc += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'cert_storage/src/cert_storage.cpp',
|
||||
'ContentSignatureVerifier.cpp',
|
||||
'CryptoTask.cpp',
|
||||
'CSTrustDomain.cpp',
|
||||
@ -183,6 +181,21 @@ UNIFIED_SOURCES += [
|
||||
'md4.c',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_NEW_CERT_STORAGE']:
|
||||
XPIDL_SOURCES += [
|
||||
'nsICertStorage.idl',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'cert_storage/src/cert_storage.cpp',
|
||||
]
|
||||
else:
|
||||
XPIDL_SOURCES += [
|
||||
'nsICertBlocklist.idl',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'CertBlocklist.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
64
security/manager/ssl/nsICertBlocklist.idl
Normal file
64
security/manager/ssl/nsICertBlocklist.idl
Normal file
@ -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();
|
||||
};
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include "nsNSSModule.h"
|
||||
|
||||
#ifndef MOZ_NEW_CERT_STORAGE
|
||||
# include "CertBlocklist.h"
|
||||
#endif
|
||||
#include "ContentSignatureVerifier.h"
|
||||
#include "NSSErrorsService.h"
|
||||
#include "OSKeyStore.h"
|
||||
@ -147,6 +150,10 @@ IMPL(nsRandomGenerator, nullptr, ProcessRestriction::AnyProcess)
|
||||
IMPL(TransportSecurityInfo, nullptr, ProcessRestriction::AnyProcess)
|
||||
IMPL(nsSiteSecurityService, &nsSiteSecurityService::Init,
|
||||
ProcessRestriction::AnyProcess, ThreadRestriction::MainThreadOnly)
|
||||
#ifndef MOZ_NEW_CERT_STORAGE
|
||||
IMPL(CertBlocklist, &CertBlocklist::Init, ProcessRestriction::ParentProcessOnly,
|
||||
ThreadRestriction::MainThreadOnly)
|
||||
#endif
|
||||
IMPL(OSKeyStore, nullptr, ProcessRestriction::ParentProcessOnly,
|
||||
ThreadRestriction::MainThreadOnly)
|
||||
IMPL(OSReauthenticator, nullptr, ProcessRestriction::ParentProcessOnly,
|
||||
|
@ -8,6 +8,7 @@
|
||||
// certificates are valid for or what errors prevented the certificates from
|
||||
// being verified.
|
||||
|
||||
var { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
add_task(async function testCAandTitle() {
|
||||
@ -100,16 +101,24 @@ 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);
|
||||
let result = await new Promise((resolve) =>
|
||||
certBlocklist.setRevocations([{
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsISubjectAndPubKeyRevocationState]),
|
||||
subject: "MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked
|
||||
pubKey: "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", // hash of the shared key
|
||||
state: Ci.nsICertStorage.STATE_ENFORCE, // yes, we want this to be revoked
|
||||
}], resolve));
|
||||
Assert.equal(result, Cr.NS_OK, "setting revocation state should succeed");
|
||||
if (AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
let certBlocklist = Cc["@mozilla.org/security/certstorage;1"]
|
||||
.getService(Ci.nsICertStorage);
|
||||
let result = await new Promise((resolve) =>
|
||||
certBlocklist.setRevocations([{
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsISubjectAndPubKeyRevocationState]),
|
||||
subject: "MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked
|
||||
pubKey: "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", // hash of the shared key
|
||||
state: Ci.nsICertStorage.STATE_ENFORCE, // yes, we want this to be revoked
|
||||
}], resolve));
|
||||
Assert.equal(result, Cr.NS_OK, "setting revocation state should succeed");
|
||||
} else {
|
||||
let certBlocklist = Cc["@mozilla.org/security/certblocklist;1"]
|
||||
.getService(Ci.nsICertBlocklist);
|
||||
certBlocklist.revokeCertBySubjectAndPubKey(
|
||||
"MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=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
|
||||
|
@ -195,14 +195,90 @@ 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 = AppConstants.MOZ_NEW_CERT_STORAGE ?
|
||||
Cc["@mozilla.org/security/certstorage;1"].getService(Ci.nsICertStorage) :
|
||||
Cc["@mozilla.org/security/certblocklist;1"].getService(Ci.nsICertBlocklist);
|
||||
|
||||
let expected = { "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5":
|
||||
{ "\tVCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=": true },
|
||||
"MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=":
|
||||
{ " Rym6o+VN9xgZXT/QLrvN/nv1ZN4=": true},
|
||||
"MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=":
|
||||
{ " a0X7/7DlTaedpgrIJg25iBPOkIM=": true},
|
||||
"MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl":
|
||||
{ " Tg==": true,
|
||||
" Hw==": true },
|
||||
};
|
||||
|
||||
add_task(async function() {
|
||||
// check some existing items in revocations.txt are blocked.
|
||||
@ -272,6 +348,12 @@ function run_test() {
|
||||
file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem";
|
||||
await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
|
||||
|
||||
if (!AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
// 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
|
||||
file = "test_onecrl/test-int-ee.pem";
|
||||
await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
|
||||
@ -294,9 +376,31 @@ 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);
|
||||
|
||||
if (!AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
// check that save with no further update is a no-op
|
||||
let lastModified = gRevocations.lastModifiedTime;
|
||||
// add an already existing entry
|
||||
certList.revokeCertByIssuerAndSerial("MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl",
|
||||
"Hw==");
|
||||
certList.saveEntries();
|
||||
let newModified = gRevocations.lastModifiedTime;
|
||||
equal(lastModified, newModified,
|
||||
"saveEntries with no modifications should not update the backing file");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
add_test({
|
||||
skip_if: () => AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, function() {
|
||||
// Check the blocklist entry has not changed
|
||||
check_revocations_txt_contents(expected);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function() {
|
||||
ok(certList.isBlocklistFresh(), "Blocklist should be fresh.");
|
||||
});
|
||||
|
||||
|
@ -9,7 +9,9 @@
|
||||
|
||||
do_get_profile();
|
||||
|
||||
var certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(Ci.nsICertStorage);
|
||||
if (AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
this.certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(Ci.nsICertStorage);
|
||||
}
|
||||
|
||||
async function addCertBySubject(cert, subject) {
|
||||
let result = await new Promise((resolve) => {
|
||||
@ -46,7 +48,9 @@ function getLongString(uniquePart, length) {
|
||||
return String(uniquePart).padStart(length, "0");
|
||||
}
|
||||
|
||||
add_task(async function test_common_subject() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_common_subject() {
|
||||
await addCertBySubject("some certificate bytes 1", "some common subject");
|
||||
await addCertBySubject("some certificate bytes 2", "some common subject");
|
||||
await addCertBySubject("some certificate bytes 3", "some common subject");
|
||||
@ -68,7 +72,9 @@ add_task(async function test_common_subject() {
|
||||
Assert.deepEqual(storedOtherCertsAsStrings, expectedOtherCerts, "should have other certificate");
|
||||
});
|
||||
|
||||
add_task(async function test_many_entries() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_many_entries() {
|
||||
const NUM_CERTS = 500;
|
||||
const CERT_LENGTH = 3000;
|
||||
const SUBJECT_LENGTH = 40;
|
||||
@ -85,7 +91,9 @@ add_task(async function test_many_entries() {
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_removal() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_removal() {
|
||||
// As long as cert_storage is given valid base64, attempting to delete some nonexistent
|
||||
// certificate will "succeed" (it'll do nothing).
|
||||
await removeCertByHash(btoa("thishashisthewrongsize"));
|
||||
|
@ -7,6 +7,10 @@
|
||||
// Tests that cert_storage properly handles its preference values.
|
||||
|
||||
function run_test() {
|
||||
if (!AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(Ci.nsICertStorage);
|
||||
// Since none of our prefs start with values, looking them up will fail. cert_storage should use
|
||||
// safe fallbacks.
|
||||
|
@ -7,12 +7,16 @@
|
||||
"use strict";
|
||||
do_get_profile(); // must be called before getting nsIX509CertDB
|
||||
|
||||
const {RemoteSecuritySettings} = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
|
||||
const {RemoteSettings} = ChromeUtils.import("resource://services-settings/remote-settings.js");
|
||||
const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
|
||||
const {TelemetryTestUtils} = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm");
|
||||
|
||||
let remoteSecSetting = new RemoteSecuritySettings();
|
||||
let remoteSecSetting;
|
||||
if (AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
const {RemoteSecuritySettings} = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
|
||||
remoteSecSetting = new RemoteSecuritySettings();
|
||||
}
|
||||
|
||||
let server;
|
||||
|
||||
let intermediate1Data;
|
||||
@ -213,7 +217,9 @@ function setupKintoPreloadServer(certGenerator, options = {
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_preload_empty() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_preload_empty() {
|
||||
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
|
||||
|
||||
let countDownloadAttempts = 0;
|
||||
@ -240,7 +246,9 @@ add_task(async function test_preload_empty() {
|
||||
certificateUsageSSLServer);
|
||||
});
|
||||
|
||||
add_task(async function test_preload_disabled() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_preload_disabled() {
|
||||
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, false);
|
||||
|
||||
let countDownloadAttempts = 0;
|
||||
@ -254,7 +262,9 @@ add_task(async function test_preload_disabled() {
|
||||
equal(countDownloadAttempts, 0, "There should have been no downloads");
|
||||
});
|
||||
|
||||
add_task(async function test_preload_invalid_hash() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_preload_invalid_hash() {
|
||||
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
|
||||
const invalidHash = "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d";
|
||||
|
||||
@ -294,7 +304,9 @@ add_task(async function test_preload_invalid_hash() {
|
||||
certificateUsageSSLServer);
|
||||
});
|
||||
|
||||
add_task(async function test_preload_invalid_length() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_preload_invalid_length() {
|
||||
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
|
||||
|
||||
let countDownloadAttempts = 0;
|
||||
@ -333,7 +345,9 @@ add_task(async function test_preload_invalid_length() {
|
||||
certificateUsageSSLServer);
|
||||
});
|
||||
|
||||
add_task(async function test_preload_basic() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_preload_basic() {
|
||||
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
|
||||
Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 100);
|
||||
|
||||
@ -379,7 +393,9 @@ add_task(async function test_preload_basic() {
|
||||
});
|
||||
|
||||
|
||||
add_task(async function test_preload_200() {
|
||||
add_task({
|
||||
skip_if: () => !AppConstants.MOZ_NEW_CERT_STORAGE,
|
||||
}, async function test_preload_200() {
|
||||
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
|
||||
Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 100);
|
||||
|
||||
|
@ -8,8 +8,8 @@ var EXPORTED_SYMBOLS = [
|
||||
"initialize",
|
||||
];
|
||||
|
||||
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { RemoteSecuritySettings } = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "RemoteSettings", "resource://services-settings/remote-settings.js");
|
||||
@ -73,39 +73,60 @@ function setRevocations(certStorage, revocations) {
|
||||
*
|
||||
* @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);
|
||||
let items = [];
|
||||
const updateCertBlocklist = AppConstants.MOZ_NEW_CERT_STORAGE ?
|
||||
async function({ data: { created, updated, deleted } }) {
|
||||
const certList = Cc["@mozilla.org/security/certstorage;1"]
|
||||
.getService(Ci.nsICertStorage);
|
||||
let items = [];
|
||||
|
||||
for (let item of deleted) {
|
||||
if (item.issuerName && item.serialNumber) {
|
||||
items.push(new IssuerAndSerialRevocationState(item.issuerName,
|
||||
item.serialNumber, Ci.nsICertStorage.STATE_UNSET));
|
||||
} else if (item.subject && item.pubKeyHash) {
|
||||
items.push(new SubjectAndPubKeyRevocationState(item.subject,
|
||||
item.pubKeyHash, Ci.nsICertStorage.STATE_UNSET));
|
||||
for (let item of deleted) {
|
||||
if (item.issuerName && item.serialNumber) {
|
||||
items.push(new IssuerAndSerialRevocationState(item.issuerName,
|
||||
item.serialNumber, Ci.nsICertStorage.STATE_UNSET));
|
||||
} else if (item.subject && item.pubKeyHash) {
|
||||
items.push(new SubjectAndPubKeyRevocationState(item.subject,
|
||||
item.pubKeyHash, Ci.nsICertStorage.STATE_UNSET));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toAdd = created.concat(updated.map(u => u.new));
|
||||
const toAdd = created.concat(updated.map(u => u.new));
|
||||
|
||||
for (let item of toAdd) {
|
||||
if (item.issuerName && item.serialNumber) {
|
||||
items.push(new IssuerAndSerialRevocationState(item.issuerName,
|
||||
item.serialNumber, Ci.nsICertStorage.STATE_ENFORCE));
|
||||
} else if (item.subject && item.pubKeyHash) {
|
||||
items.push(new SubjectAndPubKeyRevocationState(item.subject,
|
||||
item.pubKeyHash, Ci.nsICertStorage.STATE_ENFORCE));
|
||||
for (let item of toAdd) {
|
||||
if (item.issuerName && item.serialNumber) {
|
||||
items.push(new IssuerAndSerialRevocationState(item.issuerName,
|
||||
item.serialNumber, Ci.nsICertStorage.STATE_ENFORCE));
|
||||
} else if (item.subject && item.pubKeyHash) {
|
||||
items.push(new SubjectAndPubKeyRevocationState(item.subject,
|
||||
item.pubKeyHash, Ci.nsICertStorage.STATE_ENFORCE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await setRevocations(certList, items);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await setRevocations(certList, items);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
} : async function({ 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.revokeCertByIssuerAndSerial(item.issuerName,
|
||||
item.serialNumber);
|
||||
} else if (item.subject && item.pubKeyHash) {
|
||||
certList.revokeCertBySubjectAndPubKey(item.subject,
|
||||
item.pubKeyHash);
|
||||
}
|
||||
} catch (e) {
|
||||
// prevent errors relating to individual blocklist entries from
|
||||
// causing sync to fail. We will accumulate telemetry on these failures in
|
||||
// bug 1254099.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
certList.saveEntries();
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify the appropriate security pins based on records from the remote
|
||||
@ -256,9 +277,22 @@ function initialize() {
|
||||
});
|
||||
PinningBlocklistClient.on("sync", updatePinningList);
|
||||
|
||||
// In Bug 1526018 this will move into its own service, as it's not quite like
|
||||
// the others.
|
||||
RemoteSecuritySettingsClient = new RemoteSecuritySettings();
|
||||
if (AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
const { RemoteSecuritySettings } = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
|
||||
|
||||
// In Bug 1526018 this will move into its own service, as it's not quite like
|
||||
// the others.
|
||||
RemoteSecuritySettingsClient = new RemoteSecuritySettings();
|
||||
|
||||
return {
|
||||
OneCRLBlocklistClient,
|
||||
AddonBlocklistClient,
|
||||
PluginBlocklistClient,
|
||||
GfxBlocklistClient,
|
||||
PinningBlocklistClient,
|
||||
RemoteSecuritySettingsClient,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
OneCRLBlocklistClient,
|
||||
@ -266,6 +300,5 @@ function initialize() {
|
||||
PluginBlocklistClient,
|
||||
GfxBlocklistClient,
|
||||
PinningBlocklistClient,
|
||||
RemoteSecuritySettingsClient,
|
||||
};
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ gecko_profiler = ["gkrust-shared/gecko_profiler"]
|
||||
gecko_profiler_parse_elf = ["gkrust-shared/gecko_profiler_parse_elf"]
|
||||
bitsdownload = ["gkrust-shared/bitsdownload"]
|
||||
new_xulstore = ["gkrust-shared/new_xulstore"]
|
||||
new_cert_storage = ["gkrust-shared/new_cert_storage"]
|
||||
|
||||
[dependencies]
|
||||
bench-collections-gtest = { path = "../../../../xpcom/rust/gtest/bench-collections" }
|
||||
|
@ -25,6 +25,7 @@ gecko_profiler = ["gkrust-shared/gecko_profiler"]
|
||||
gecko_profiler_parse_elf = ["gkrust-shared/gecko_profiler_parse_elf"]
|
||||
bitsdownload = ["gkrust-shared/bitsdownload"]
|
||||
new_xulstore = ["gkrust-shared/new_xulstore"]
|
||||
new_cert_storage = ["gkrust-shared/new_cert_storage"]
|
||||
|
||||
[dependencies]
|
||||
gkrust-shared = { path = "shared" }
|
||||
|
@ -53,3 +53,6 @@ if CONFIG['MOZ_BITS_DOWNLOAD']:
|
||||
|
||||
if CONFIG['MOZ_NEW_XULSTORE']:
|
||||
gkrust_features += ['new_xulstore']
|
||||
|
||||
if CONFIG['MOZ_NEW_CERT_STORAGE']:
|
||||
gkrust_features += ['new_cert_storage']
|
||||
|
@ -34,7 +34,7 @@ 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" }
|
||||
cert_storage = { path = "../../../../security/manager/ssl/cert_storage", optional = true }
|
||||
bitsdownload = { path = "../../../components/bitsdownload", optional = true }
|
||||
storage = { path = "../../../../storage/rust" }
|
||||
bookmark_sync = { path = "../../../components/places/bookmark_sync", optional = true }
|
||||
@ -62,6 +62,7 @@ cranelift_none = ["jsrust_shared/cranelift_none"]
|
||||
gecko_profiler = ["profiler_helper"]
|
||||
gecko_profiler_parse_elf = ["profiler_helper/parse_elf"]
|
||||
new_xulstore = ["xulstore"]
|
||||
new_cert_storage = ["cert_storage"]
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
@ -31,6 +31,7 @@ extern crate env_logger;
|
||||
extern crate u2fhid;
|
||||
extern crate gkrust_utils;
|
||||
extern crate log;
|
||||
#[cfg(feature = "new_cert_storage")]
|
||||
extern crate cert_storage;
|
||||
extern crate cosec;
|
||||
extern crate rsdparsa_capi;
|
||||
|
@ -364,4 +364,11 @@ this.AppConstants = Object.freeze({
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_NEW_CERT_STORAGE:
|
||||
#ifdef MOZ_NEW_CERT_STORAGE
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
});
|
||||
|
@ -1750,3 +1750,15 @@ def new_notification_store(milestone):
|
||||
|
||||
set_config('MOZ_NEW_NOTIFICATION_STORE', True, when=new_notification_store)
|
||||
set_define('MOZ_NEW_NOTIFICATION_STORE', True, when=new_notification_store)
|
||||
|
||||
|
||||
# new Cert Storage implementation
|
||||
# ==============================================================
|
||||
|
||||
@depends(milestone)
|
||||
def new_cert_storage(milestone):
|
||||
if milestone.is_nightly:
|
||||
return True
|
||||
|
||||
set_config('MOZ_NEW_CERT_STORAGE', True, when=new_cert_storage)
|
||||
set_define('MOZ_NEW_CERT_STORAGE', True, when=new_cert_storage)
|
||||
|
Loading…
Reference in New Issue
Block a user