Bug 1128607 - Add freshness check for OneCRL (r=keeler)

This commit is contained in:
Mark Goodwin 2015-05-07 18:54:05 +01:00
parent 726e9673d3
commit 9e5913dddb
5 changed files with 112 additions and 18 deletions

View File

@ -1740,6 +1740,10 @@ pref("security.mixed_content.block_active_content", true);
// 1 = allow MITM for certificate pinning checks.
pref("security.cert_pinning.enforcement_level", 1);
// Required blocklist freshness for OneCRL OCSP bypass
// (default should be at least as large as extensions.blocklist.interval)
pref("security.onecrl.maximum_staleness_in_seconds", 0);
// Override the Gecko-default value of false for Firefox.
pref("plain_text.wrap_long_lines", true);

View File

@ -444,6 +444,13 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
PR_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
(cachedResponsePresent && cachedResponseResult != Success));
// If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
bool blocklistIsFresh;
nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
if (NS_FAILED(nsrv)) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
// TODO: We still need to handle the fallback for expired responses. But,
// if/when we disable OCSP fetching by default, it would be ambiguous whether
// security.OCSP.enable==0 means "I want the default" or "I really never want
@ -452,7 +459,8 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
if ((mOCSPFetching == NeverFetchOCSP) ||
(endEntityOrCA == EndEntityOrCA::MustBeCA &&
(mOCSPFetching == FetchOCSPForDVHardFail ||
mOCSPFetching == FetchOCSPForDVSoftFail))) {
mOCSPFetching == FetchOCSPForDVSoftFail ||
blocklistIsFresh))) {
// We're not going to be doing any fetching, so if there was a cached
// "unknown" response, say so.
if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {

View File

@ -14,7 +14,7 @@ interface nsIX509Cert;
/**
* Represents a service to add certificates as explicitly blocked/distrusted.
*/
[scriptable, uuid(fed30090-c190-11e4-8830-0800200c9a66)]
[scriptable, uuid(e0654480-f433-11e4-b939-0800200c9a66)]
interface nsICertBlocklist : nsISupports {
/**
* Add details of a revoked certificate :
@ -50,4 +50,12 @@ interface nsICertBlocklist : nsISupports {
in unsigned long subject_length,
[const, array, size_is(pubkey_length)] in octet pubkey,
in unsigned long pubkey_length);
/**
* 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)
*/
boolean isBlocklistFresh();
};

View File

@ -5,6 +5,7 @@
#include "CertBlocklist.h"
#include "mozilla/Base64.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCRTGlue.h"
@ -22,8 +23,17 @@
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_MAX_STALENESS_IN_SECONDS "security.onecrl.maximum_staleness_in_seconds"
static PRLogModuleInfo* gCertBlockPRLog;
uint32_t CertBlocklist::sLastBlocklistUpdate = 0U;
uint32_t CertBlocklist::sMaxStaleness = 0U;
CertBlocklistItem::CertBlocklistItem(const uint8_t* DNData,
size_t DNLength,
const uint8_t* otherData,
@ -69,11 +79,11 @@ CertBlocklistItem::ToBase64(nsACString& b64DNOut, nsACString& b64OtherOut)
mDNLength);
nsDependentCSubstring otherString(reinterpret_cast<char*>(mOtherData),
mOtherLength);
nsresult rv = mozilla::Base64Encode(DNString, b64DNOut);
nsresult rv = Base64Encode(DNString, b64DNOut);
if (NS_FAILED(rv)) {
return rv;
}
rv = mozilla::Base64Encode(otherString, b64OtherOut);
rv = Base64Encode(otherString, b64OtherOut);
return rv;
}
@ -120,6 +130,12 @@ CertBlocklist::CertBlocklist()
CertBlocklist::~CertBlocklist()
{
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
PREF_BACKGROUND_UPDATE_TIMER,
this);
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
PREF_MAX_STALENESS_IN_SECONDS,
this);
}
nsresult
@ -134,9 +150,24 @@ CertBlocklist::Init()
return NS_ERROR_NOT_SAME_THREAD;
}
// Register preference callbacks
nsresult rv =
Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
PREF_BACKGROUND_UPDATE_TIMER,
this);
if (NS_FAILED(rv)) {
return rv;
}
rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
PREF_MAX_STALENESS_IN_SECONDS,
this);
if (NS_FAILED(rv)) {
return rv;
}
// Get the profile directory
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(mBackingFile));
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(mBackingFile));
if (NS_FAILED(rv) || !mBackingFile) {
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
("CertBlocklist::Init - couldn't get profile dir"));
@ -162,7 +193,7 @@ CertBlocklist::Init()
}
nsresult
CertBlocklist::EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock)
CertBlocklist::EnsureBackingFileInitialized(MutexAutoLock& lock)
{
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
("CertBlocklist::EnsureBackingFileInitialized"));
@ -262,7 +293,7 @@ CertBlocklist::RevokeCertBySubjectAndPubKey(const char* aSubject,
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
("CertBlocklist::RevokeCertBySubjectAndPubKey - subject is: %s and pubKeyHash: %s",
aSubject, aPubKeyHash));
mozilla::MutexAutoLock lock(mMutex);
MutexAutoLock lock(mMutex);
return AddRevokedCertInternal(nsDependentCString(aSubject),
nsDependentCString(aPubKeyHash),
@ -278,7 +309,7 @@ CertBlocklist::RevokeCertByIssuerAndSerial(const char* aIssuer,
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
("CertBlocklist::RevokeCertByIssuerAndSerial - issuer is: %s and serial: %s",
aIssuer, aSerialNumber));
mozilla::MutexAutoLock lock(mMutex);
MutexAutoLock lock(mMutex);
return AddRevokedCertInternal(nsDependentCString(aIssuer),
nsDependentCString(aSerialNumber),
@ -291,16 +322,16 @@ CertBlocklist::AddRevokedCertInternal(const nsACString& aEncodedDN,
const nsACString& aEncodedOther,
CertBlocklistItemMechanism aMechanism,
CertBlocklistItemState aItemState,
mozilla::MutexAutoLock& /*proofOfLock*/)
MutexAutoLock& /*proofOfLock*/)
{
nsCString decodedDN;
nsCString decodedOther;
nsresult rv = mozilla::Base64Decode(aEncodedDN, decodedDN);
nsresult rv = Base64Decode(aEncodedDN, decodedDN);
if (NS_FAILED(rv)) {
return rv;
}
rv = mozilla::Base64Decode(aEncodedOther, decodedOther);
rv = Base64Decode(aEncodedOther, decodedOther);
if (NS_FAILED(rv)) {
return rv;
}
@ -452,7 +483,7 @@ CertBlocklist::SaveEntries()
{
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
("CertBlocklist::SaveEntries - not initialized"));
mozilla::MutexAutoLock lock(mMutex);
MutexAutoLock lock(mMutex);
if (!mModified) {
return NS_OK;
}
@ -532,19 +563,19 @@ CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
uint32_t aPubKeyLength,
bool* _retval)
{
mozilla::MutexAutoLock lock(mMutex);
MutexAutoLock lock(mMutex);
nsresult rv = EnsureBackingFileInitialized(lock);
if (NS_FAILED(rv)) {
return rv;
}
mozilla::pkix::Input issuer;
mozilla::pkix::Input serial;
if (issuer.Init(aIssuer, aIssuerLength) != mozilla::pkix::Success) {
Input issuer;
Input serial;
if (issuer.Init(aIssuer, aIssuerLength) != Success) {
return NS_ERROR_FAILURE;
}
if (serial.Init(aSerial, aSerialLength) != mozilla::pkix::Success) {
if (serial.Init(aSerial, aSerialLength) != Success) {
return NS_ERROR_FAILURE;
}
@ -585,3 +616,43 @@ CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
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);
if (now > sLastBlocklistUpdate) {
int64_t interval = now - sLastBlocklistUpdate;
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
("CertBlocklist::IsBlocklistFresh we're after the last BlocklistUpdate "
"interval is %i, staleness %u", interval, sMaxStaleness));
*_retval = sMaxStaleness > interval;
}
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
("CertBlocklist::IsBlocklistFresh ? %s", *_retval ? "true" : "false"));
return NS_OK;
}
/* static */
void
CertBlocklist::PreferenceChanged(const char* aPref, void* aClosure)
{
CertBlocklist* blocklist = reinterpret_cast<CertBlocklist*>(aClosure);
MutexAutoLock lock(blocklist->mMutex);
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
("CertBlocklist::PreferenceChanged %s changed", aPref));
if (strcmp(aPref, PREF_BACKGROUND_UPDATE_TIMER) == 0) {
sLastBlocklistUpdate = Preferences::GetUint(PREF_BACKGROUND_UPDATE_TIMER,
uint32_t(0));
} else if (strcmp(aPref, PREF_MAX_STALENESS_IN_SECONDS) == 0) {
sMaxStaleness = Preferences::GetUint(PREF_MAX_STALENESS_IN_SECONDS,
uint32_t(0));
}
}

View File

@ -78,6 +78,9 @@ private:
nsCOMPtr<nsIFile> mBackingFile;
protected:
static void PreferenceChanged(const char* aPref, void* aClosure);
static uint32_t sLastBlocklistUpdate;
static uint32_t sMaxStaleness;
virtual ~CertBlocklist();
};