зеркало из https://github.com/mozilla/gecko-dev.git
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)
This commit is contained in:
Родитель
344ea4ee7c
Коммит
c3cd918c5c
|
@ -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)",
|
||||
|
|
|
@ -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<nsISupports> sss = do_GetService(NS_SSSERVICE_CONTRACTID);
|
||||
nsCOMPtr<nsISupports> cbl = do_GetService(NS_CERTSTORAGE_CONTRACTID);
|
||||
nsCOMPtr<nsISupports> cbl = do_GetService(NS_CERTBLOCKLIST_CONTRACTID);
|
||||
nsCOMPtr<nsISupports> cos = do_GetService(NS_CERTOVERRIDE_CONTRACTID);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<nsICertStorage> mCertBlocklist;
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
|
||||
// Certificate Transparency data extracted during certificate verification
|
||||
UniqueSECItem mSCTListFromCertificate;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<nsICertStorage> mCertBlocklist;
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
|
|
|
@ -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<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));
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "cert_storage"
|
||||
version = "0.0.1"
|
||||
authors = ["Dana Keeler <dkeeler@mozilla.com>", "Mark Goodwin <mgoodwin@mozilla.com"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.10"
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
rkv = "0.9.2"
|
||||
sha2 = "^0.7"
|
||||
style = { path = "../../../../servo/components/style" }
|
||||
time = "0.1"
|
||||
xpcom = { path = "../../../../xpcom/rust/xpcom" }
|
|
@ -1,23 +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 "cert_storage.h"
|
||||
|
||||
nsresult construct_cert_storage(nsISupports* outer, REFNSIID iid,
|
||||
void** result) {
|
||||
// Forward to the main thread synchronously.
|
||||
nsCOMPtr<nsIThread> 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;
|
||||
}
|
|
@ -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_
|
|
@ -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<u8> {
|
||||
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<T: Display> From<T> 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<String, i32>,
|
||||
}
|
||||
|
||||
impl SecurityState {
|
||||
pub fn new(profile_path: PathBuf) -> Result<SecurityState, SecurityStateError> {
|
||||
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<Vec<u8>> = 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<Option<i16>, 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<i16, SecurityStateError> {
|
||||
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<i16, SecurityStateError> {
|
||||
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<i16, SecurityStateError> {
|
||||
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<bool, SecurityStateError> {
|
||||
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<bool, SecurityStateError> {
|
||||
self.is_data_fresh(
|
||||
"services.blocklist.onecrl.checked",
|
||||
"security.onecrl.maximum_staleness_in_seconds",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_whitelist_fresh(&self) -> Result<bool, SecurityStateError> {
|
||||
self.is_data_fresh(
|
||||
"services.blocklist.intermediates.checked",
|
||||
"security.onecrl.maximum_staleness_in_seconds",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_enrollment_fresh(&self) -> Result<bool, SecurityStateError> {
|
||||
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<PathBuf, SecurityStateError> {
|
||||
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::<nsIFile>::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<i32, SecurityStateError> {
|
||||
let pref_service = match xpcom::services::get_PreferencesService() {
|
||||
Some(ps) => ps,
|
||||
_ => {
|
||||
return Err(SecurityStateError::from(
|
||||
"could not get preferences service",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let prefs: RefPtr<nsIPrefBranch> = 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<SecurityState>,
|
||||
}
|
||||
|
||||
#[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<nsIPrefBranch> = 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::<nsIObserver>(), 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<nsIPrefBranch> = 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
|
||||
}
|
||||
}
|
|
@ -157,10 +157,10 @@ Classes = [
|
|||
'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': '{11aefd53-2fbb-4c92-a0c1-053212ae42d0}',
|
||||
'contract_ids': ['@mozilla.org/security/certblocklist;1'],
|
||||
'type': 'CertBlocklist',
|
||||
'legacy_constructor': 'mozilla::psm::NSSConstructor<CertBlocklist>',
|
||||
},
|
||||
{
|
||||
'cid': '{57972956-5718-42d2-8070-b3fc72212eaf}',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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();
|
||||
};
|
|
@ -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();
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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"
|
||||
|
|
Загрузка…
Ссылка в новой задаче