mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-11-05 02:06:25 +00:00
674 lines
21 KiB
C++
674 lines
21 KiB
C++
/* -*- 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/Base64.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/unused.h"
|
|
#include "nsAppDirectoryServiceDefs.h"
|
|
#include "nsCRTGlue.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 "nsTHashtable.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "pkix/Input.h"
|
|
#include "mozilla/Logging.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_KINTO_ONECRL_CHECKED "services.kinto.onecrl.checked"
|
|
#define PREF_MAX_STALENESS_IN_SECONDS "security.onecrl.maximum_staleness_in_seconds"
|
|
#define PREF_ONECRL_VIA_AMO "security.onecrl.via.amo"
|
|
|
|
static PRLogModuleInfo* gCertBlockPRLog;
|
|
|
|
uint32_t CertBlocklist::sLastBlocklistUpdate = 0U;
|
|
uint32_t CertBlocklist::sLastKintoUpdate = 0U;
|
|
uint32_t CertBlocklist::sMaxStaleness = 0U;
|
|
bool CertBlocklist::sUseAMO = true;
|
|
|
|
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(reinterpret_cast<char*>(mDNData),
|
|
mDNLength);
|
|
nsDependentCSubstring otherString(reinterpret_cast<char*>(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)
|
|
{
|
|
if (!gCertBlockPRLog) {
|
|
gCertBlockPRLog = PR_NewLogModule("CertBlock");
|
|
}
|
|
}
|
|
|
|
CertBlocklist::~CertBlocklist()
|
|
{
|
|
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
|
PREF_BACKGROUND_UPDATE_TIMER,
|
|
this);
|
|
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
|
PREF_MAX_STALENESS_IN_SECONDS,
|
|
this);
|
|
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
|
PREF_ONECRL_VIA_AMO,
|
|
this);
|
|
Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged,
|
|
PREF_KINTO_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_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;
|
|
}
|
|
rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
|
|
PREF_ONECRL_VIA_AMO,
|
|
this);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged,
|
|
PREF_KINTO_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->GetNativePath(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 char* aSubject,
|
|
const char* aPubKeyHash)
|
|
{
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
|
("CertBlocklist::RevokeCertBySubjectAndPubKey - subject is: %s and pubKeyHash: %s",
|
|
aSubject, aPubKeyHash));
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
return AddRevokedCertInternal(nsDependentCString(aSubject),
|
|
nsDependentCString(aPubKeyHash),
|
|
BlockBySubjectAndPubKey,
|
|
CertNewFromBlocklist, lock);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CertBlocklist::RevokeCertByIssuerAndSerial(const char* aIssuer,
|
|
const char* aSerialNumber)
|
|
{
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Debug,
|
|
("CertBlocklist::RevokeCertByIssuerAndSerial - issuer is: %s and serial: %s",
|
|
aIssuer, aSerialNumber));
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
return AddRevokedCertInternal(nsDependentCString(aIssuer),
|
|
nsDependentCString(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(reinterpret_cast<const uint8_t*>(decodedDN.get()),
|
|
decodedDN.Length(),
|
|
reinterpret_cast<const uint8_t*>(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.RemoveAndForget(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);
|
|
NS_ASSERTION(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 uint8_t* aIssuer,
|
|
uint32_t aIssuerLength,
|
|
const uint8_t* aSerial,
|
|
uint32_t aSerialLength,
|
|
const uint8_t* aSubject,
|
|
uint32_t aSubjectLength,
|
|
const uint8_t* aPubKey,
|
|
uint32_t aPubKeyLength,
|
|
bool* _retval)
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
|
("CertBlocklist::IsCertRevoked?"));
|
|
nsresult rv = EnsureBackingFileInitialized(lock);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
Input issuer;
|
|
Input serial;
|
|
if (issuer.Init(aIssuer, aIssuerLength) != Success) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
if (serial.Init(aSerial, aSerialLength) != Success) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
CertBlocklistItem issuerSerial(aIssuer, aIssuerLength, aSerial, aSerialLength,
|
|
BlockByIssuerAndSerial);
|
|
|
|
nsAutoCString encDN;
|
|
nsAutoCString encOther;
|
|
|
|
issuerSerial.ToBase64(encDN, encOther);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
|
("CertBlocklist::IsCertRevoked issuer %s - serial %s",
|
|
encDN.get(), encOther.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(reinterpret_cast<const unsigned char*>(aPubKey),
|
|
aPubKeyLength);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
nsCString hashString;
|
|
rv = crypto->Finish(false, hashString);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
CertBlocklistItem subjectPubKey(aSubject,
|
|
static_cast<size_t>(aSubjectLength),
|
|
reinterpret_cast<const uint8_t*>(hashString.get()),
|
|
hashString.Length(),
|
|
BlockBySubjectAndPubKey);
|
|
|
|
rv = subjectPubKey.ToBase64(encDN, encOther);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
|
("CertBlocklist::IsCertRevoked subject %s - pubKey hash %s",
|
|
encDN.get(), encOther.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);
|
|
uint32_t lastUpdate = sUseAMO ? sLastBlocklistUpdate : sLastKintoUpdate;
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
|
("CertBlocklist::IsBlocklistFresh using AMO? %i lastUpdate is %i",
|
|
sUseAMO, lastUpdate));
|
|
|
|
if (now > lastUpdate) {
|
|
int64_t interval = now - lastUpdate;
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
|
("CertBlocklist::IsBlocklistFresh we're after the last BlocklistUpdate "
|
|
"interval is %i, 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, void* aClosure)
|
|
|
|
{
|
|
CertBlocklist* blocklist = reinterpret_cast<CertBlocklist*>(aClosure);
|
|
MutexAutoLock lock(blocklist->mMutex);
|
|
|
|
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
|
("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_KINTO_ONECRL_CHECKED) == 0) {
|
|
sLastKintoUpdate = Preferences::GetUint(PREF_KINTO_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));
|
|
} else if (strcmp(aPref, PREF_ONECRL_VIA_AMO) == 0) {
|
|
sUseAMO = Preferences::GetBool(PREF_ONECRL_VIA_AMO, true);
|
|
}
|
|
}
|