mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1024809 - (OneCRL) Create a blocklist mechanism to revoke intermediate certs. r=keeler,Unfocused
This commit is contained in:
parent
d7fab6a6dd
commit
e1eaa1f5df
@ -9,12 +9,13 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ExtendedValidation.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "NSSErrorsService.h"
|
||||
#include "OCSPRequestor.h"
|
||||
#include "certdb.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nss.h"
|
||||
#include "NSSErrorsService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "pk11pub.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkix/pkixnss.h"
|
||||
@ -68,6 +69,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
|
||||
, mMinimumNonECCBits(forEV ? MINIMUM_NON_ECC_BITS_EV : MINIMUM_NON_ECC_BITS_DV)
|
||||
, mHostname(hostname)
|
||||
, mBuiltChain(builtChain)
|
||||
, mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
|
||||
{
|
||||
}
|
||||
|
||||
@ -338,6 +340,27 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
|
||||
maxOCSPLifetimeInDays = 365;
|
||||
}
|
||||
|
||||
if (!mCertBlocklist) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
bool isCertRevoked;
|
||||
nsresult nsrv = mCertBlocklist->IsCertRevoked(
|
||||
certID.issuer.UnsafeGetData(),
|
||||
certID.issuer.GetLength(),
|
||||
certID.serialNumber.UnsafeGetData(),
|
||||
certID.serialNumber.GetLength(),
|
||||
&isCertRevoked);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
if (isCertRevoked) {
|
||||
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
|
||||
("NSSCertDBTrustDomain: certificate is in blocklist"));
|
||||
return Result::ERROR_REVOKED_CERTIFICATE;
|
||||
}
|
||||
|
||||
// If we have a stapled OCSP response then the verification of that response
|
||||
// determines the result unless the OCSP response is expired. We make an
|
||||
// exception for expired responses because some servers, nginx in particular,
|
||||
|
@ -7,9 +7,10 @@
|
||||
#ifndef mozilla_psm__NSSCertDBTrustDomain_h
|
||||
#define mozilla_psm__NSSCertDBTrustDomain_h
|
||||
|
||||
#include "CertVerifier.h"
|
||||
#include "nsICertBlocklist.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
#include "secmodt.h"
|
||||
#include "CertVerifier.h"
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
@ -110,6 +111,7 @@ private:
|
||||
const unsigned int mMinimumNonECCBits;
|
||||
const char* mHostname; // non-owning - only used for pinning checks
|
||||
ScopedCERTCertList* mBuiltChain; // non-owning
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIBufEntropyCollector.idl',
|
||||
'nsICertBlocklist.idl',
|
||||
'nsISecurityUITelemetry.idl',
|
||||
'nsISecurityWarningDialogs.idl',
|
||||
'nsISSLStatusProvider.idl',
|
||||
|
40
security/manager/boot/public/nsICertBlocklist.idl
Normal file
40
security/manager/boot/public/nsICertBlocklist.idl
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- 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(44b0ee42-1af3-45e7-b601-7f17bd67c5cc)]
|
||||
interface nsICertBlocklist : nsISupports {
|
||||
/**
|
||||
* Add details of a revoked certificate :
|
||||
* issuer name (base-64 encoded DER) and serial number (base-64 encoded DER).
|
||||
*/
|
||||
void addRevokedCert(in string issuer, in string serialNumber);
|
||||
|
||||
/**
|
||||
* Persist (fresh) blocklist entries to the profile (if a profile directory is
|
||||
* available). Note: calling this will result in synchronous I/O.
|
||||
*/
|
||||
void saveEntries();
|
||||
|
||||
/**
|
||||
* Check if a certificate is blocked.
|
||||
* isser - issuer name, DER encoded
|
||||
* serial - serial number, DER encoded
|
||||
*/
|
||||
boolean isCertRevoked([const, array, size_is(issuer_length)] in octet issuer,
|
||||
in unsigned long issuer_length,
|
||||
[const, array, size_is(serial_length)] in octet serial,
|
||||
in unsigned long serial_length);
|
||||
};
|
469
security/manager/boot/src/CertBlocklist.cpp
Normal file
469
security/manager/boot/src/CertBlocklist.cpp
Normal file
@ -0,0 +1,469 @@
|
||||
/* -*- 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 "CertBlocklist.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "pkix/Input.h"
|
||||
#include "prlog.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(CertBlocklist, nsICertBlocklist)
|
||||
|
||||
static PRLogModuleInfo* gCertBlockPRLog;
|
||||
|
||||
CertBlocklistItem::CertBlocklistItem(mozilla::pkix::Input aIssuer,
|
||||
mozilla::pkix::Input aSerial)
|
||||
{
|
||||
mIssuerData = new uint8_t[aIssuer.GetLength()];
|
||||
memcpy(mIssuerData, aIssuer.UnsafeGetData(), aIssuer.GetLength());
|
||||
mIssuer.Init(mIssuerData, aIssuer.GetLength());
|
||||
|
||||
mSerialData = new uint8_t[aSerial.GetLength()];
|
||||
memcpy(mSerialData, aSerial.UnsafeGetData(), aSerial.GetLength());
|
||||
mSerial.Init(mSerialData, aSerial.GetLength());
|
||||
}
|
||||
|
||||
CertBlocklistItem::CertBlocklistItem(const CertBlocklistItem& aItem)
|
||||
{
|
||||
uint32_t issuerLength = aItem.mIssuer.GetLength();
|
||||
mIssuerData = new uint8_t[issuerLength];
|
||||
memcpy(mIssuerData, aItem.mIssuerData, issuerLength);
|
||||
mIssuer.Init(mIssuerData, issuerLength);
|
||||
|
||||
uint32_t serialLength = aItem.mSerial.GetLength();
|
||||
mSerialData = new uint8_t[serialLength];
|
||||
memcpy(mSerialData, aItem.mSerialData, serialLength);
|
||||
mSerial.Init(mSerialData, serialLength);
|
||||
mIsCurrent = aItem.mIsCurrent;
|
||||
}
|
||||
|
||||
CertBlocklistItem::~CertBlocklistItem()
|
||||
{
|
||||
delete[] mIssuerData;
|
||||
delete[] mSerialData;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklistItem::ToBase64(nsACString& b64IssuerOut, nsACString& b64SerialOut)
|
||||
{
|
||||
nsDependentCSubstring issuerString(reinterpret_cast<char *>(mIssuerData),
|
||||
mIssuer.GetLength());
|
||||
nsDependentCSubstring serialString(reinterpret_cast<char *>(mSerialData),
|
||||
mSerial.GetLength());
|
||||
nsresult rv = mozilla::Base64Encode(issuerString, b64IssuerOut);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = mozilla::Base64Encode(serialString, b64SerialOut);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
CertBlocklistItem::operator==(const CertBlocklistItem& aItem) const
|
||||
{
|
||||
bool retval = InputsAreEqual(aItem.mIssuer, mIssuer) &&
|
||||
InputsAreEqual(aItem.mSerial, mSerial);
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CertBlocklistItem::Hash() const
|
||||
{
|
||||
uint32_t hash;
|
||||
uint32_t serialLength = mSerial.GetLength();
|
||||
// there's no requirement for a serial to be as large as 32 bits; if it's
|
||||
// smaller, fall back to the first octet (otherwise, the last four)
|
||||
if (serialLength >= 4) {
|
||||
hash = *(uint32_t *)(mSerialData + serialLength - 4);
|
||||
} else {
|
||||
hash = *mSerialData;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
CertBlocklist::CertBlocklist()
|
||||
: mMutex("CertBlocklist::mMutex")
|
||||
, mModified(false)
|
||||
{
|
||||
if (!gCertBlockPRLog) {
|
||||
gCertBlockPRLog = PR_NewLogModule("CertBlock");
|
||||
}
|
||||
}
|
||||
|
||||
CertBlocklist::~CertBlocklist()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklist::Init()
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG, ("CertBlocklist::Init"));
|
||||
if (!NS_IsMainThread()) {
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
|
||||
("CertBlocklist::Init - called off main thread"));
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
// Load the revocations file into the cert blocklist
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
|
||||
("CertBlocklist::Init - not initialized; initializing"));
|
||||
nsresult 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"));
|
||||
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;
|
||||
}
|
||||
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
|
||||
("CertBlocklist::Init certList path: %s", path.get()));
|
||||
|
||||
bool exists = false;
|
||||
rv = mBackingFile->Exists(&exists);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
|
||||
("CertBlocklist::Init no revocations file"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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 issuer;
|
||||
nsAutoCString serial;
|
||||
// read in the revocations file. The file format is as follows: each line
|
||||
// contains a comment, base64 encoded DER for an issuer or base64 encoded DER
|
||||
// for a serial number. Comment lines start with '#', serial number lines, ' '
|
||||
// (a space) and anything else is assumed to be an issuer.
|
||||
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() != ' ') {
|
||||
issuer = line;
|
||||
continue;
|
||||
}
|
||||
serial = line;
|
||||
serial.Trim(" ", true, false, false);
|
||||
// serial numbers 'belong' to the last issuer line seen; if no issuer has
|
||||
// been seen, the serial number is ignored
|
||||
if (issuer.IsEmpty() || serial.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
|
||||
("CertBlocklist::Init adding: %s %s", issuer.get(), serial.get()));
|
||||
rv = AddRevokedCertInternal(issuer.get(),
|
||||
serial.get(),
|
||||
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
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
|
||||
("CertBlocklist::Init adding revoked cert failed"));
|
||||
}
|
||||
} while (more);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// void addRevokedCert (in string issuer, in string serialNumber);
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::AddRevokedCert(const char* aIssuer,
|
||||
const char* aSerialNumber)
|
||||
{
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_DEBUG,
|
||||
("CertBlocklist::addRevokedCert - issuer is: %s and serial: %s",
|
||||
aIssuer, aSerialNumber));
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
return AddRevokedCertInternal(aIssuer,
|
||||
aSerialNumber,
|
||||
CertNewFromBlocklist,
|
||||
lock);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CertBlocklist::AddRevokedCertInternal(const char* aIssuer,
|
||||
const char* aSerialNumber,
|
||||
CertBlocklistItemState aItemState,
|
||||
mozilla::MutexAutoLock& /*proofOfLock*/)
|
||||
{
|
||||
nsCString decodedIssuer;
|
||||
nsCString decodedSerial;
|
||||
|
||||
nsresult rv;
|
||||
rv = mozilla::Base64Decode(nsDependentCString(aIssuer), decodedIssuer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = mozilla::Base64Decode(nsDependentCString(aSerialNumber), decodedSerial);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mozilla::pkix::Input issuer;
|
||||
mozilla::pkix::Input serial;
|
||||
|
||||
mozilla::pkix::Result pkrv;
|
||||
pkrv = issuer.Init(reinterpret_cast<const uint8_t*>(decodedIssuer.get()),
|
||||
decodedIssuer.Length());
|
||||
if (pkrv != mozilla::pkix::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
pkrv = serial.Init(reinterpret_cast<const uint8_t*>(decodedSerial.get()),
|
||||
decodedSerial.Length());
|
||||
if (pkrv != mozilla::pkix::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CertBlocklistItem item(issuer, serial);
|
||||
|
||||
if (aItemState == CertNewFromBlocklist) {
|
||||
// we want SaveEntries to be a no-op if no new entries are added
|
||||
if (!mBlocklist.Contains(item)) {
|
||||
mModified = true;
|
||||
}
|
||||
|
||||
// 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(item);
|
||||
item.mIsCurrent = true;
|
||||
}
|
||||
mBlocklist.PutEntry(item);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Data needed for writing blocklist items out to the revocations file
|
||||
struct BlocklistSaveInfo
|
||||
{
|
||||
IssuerTable issuerTable;
|
||||
BlocklistStringSet issuers;
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
bool success = true;
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// sort blocklist items into lists of serials for each issuer
|
||||
PLDHashOperator
|
||||
ProcessEntry(BlocklistItemKey* aHashKey, void* aUserArg)
|
||||
{
|
||||
BlocklistSaveInfo* saveInfo = reinterpret_cast<BlocklistSaveInfo*>(aUserArg);
|
||||
CertBlocklistItem item = aHashKey->GetKey();
|
||||
|
||||
if (!item.mIsCurrent) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsAutoCString encIssuer;
|
||||
nsAutoCString encSerial;
|
||||
|
||||
nsresult rv = item.ToBase64(encIssuer, encSerial);
|
||||
if (NS_FAILED(rv)) {
|
||||
saveInfo->success = false;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
saveInfo->issuers.PutEntry(encIssuer);
|
||||
BlocklistStringSet* issuerSet = saveInfo->issuerTable.Get(encIssuer);
|
||||
if (!issuerSet) {
|
||||
issuerSet = new BlocklistStringSet();
|
||||
saveInfo->issuerTable.Put(encIssuer, issuerSet);
|
||||
}
|
||||
issuerSet->PutEntry(encSerial);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// write serial data to the output stream
|
||||
PLDHashOperator
|
||||
WriteSerial(nsCStringHashKey* aHashKey, void* aUserArg)
|
||||
{
|
||||
BlocklistSaveInfo* saveInfo = reinterpret_cast<BlocklistSaveInfo*>(aUserArg);
|
||||
|
||||
nsresult rv = WriteLine(saveInfo->outputStream,
|
||||
NS_LITERAL_CSTRING(" ") + aHashKey->GetKey());
|
||||
if (NS_FAILED(rv)) {
|
||||
saveInfo->success = false;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Write issuer data to the output stream
|
||||
PLDHashOperator
|
||||
WriteIssuer(nsCStringHashKey* aHashKey, void* aUserArg)
|
||||
{
|
||||
BlocklistSaveInfo* saveInfo = reinterpret_cast<BlocklistSaveInfo *>(aUserArg);
|
||||
nsAutoPtr<BlocklistStringSet> issuerSet;
|
||||
|
||||
saveInfo->issuerTable.RemoveAndForget(aHashKey->GetKey(), issuerSet);
|
||||
|
||||
nsresult rv = WriteLine(saveInfo->outputStream, aHashKey->GetKey());
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
issuerSet->EnumerateEntries(WriteSerial, saveInfo);
|
||||
if (!saveInfo->success) {
|
||||
saveInfo->success = false;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
if (!mModified) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (!mBackingFile) {
|
||||
// We allow this to succeed with no profile directory for tests
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
|
||||
("CertBlocklist::SaveEntries no file in profile to write to"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
BlocklistSaveInfo saveInfo;
|
||||
nsresult rv;
|
||||
rv = NS_NewAtomicFileOutputStream(getter_AddRefs(saveInfo.outputStream),
|
||||
mBackingFile, -1, -1, 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
mBlocklist.EnumerateEntries(ProcessEntry, &saveInfo);
|
||||
if (!saveInfo.success) {
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
|
||||
("CertBlocklist::SaveEntries writing revocation data failed"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = WriteLine(saveInfo.outputStream,
|
||||
NS_LITERAL_CSTRING("# Auto generated contents. Do not edit."));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
saveInfo.issuers.EnumerateEntries(WriteIssuer, &saveInfo);
|
||||
if (!saveInfo.success) {
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
|
||||
("CertBlocklist::SaveEntries writing revocation data failed"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeStream =
|
||||
do_QueryInterface(saveInfo.outputStream);
|
||||
NS_ASSERTION(safeStream, "expected a safe output stream!");
|
||||
if (!safeStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
rv = safeStream->Finish();
|
||||
if (NS_FAILED(rv)) {
|
||||
PR_LOG(gCertBlockPRLog, PR_LOG_WARN,
|
||||
("CertBlocklist::SaveEntries saving revocation data failed"));
|
||||
return rv;
|
||||
}
|
||||
mModified = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// boolean isCertRevoked([const, array, size_is(issuerLength)] in octet issuer,
|
||||
// in unsigned long issuerLength,
|
||||
// [const, array, size_is(serialLength)] in octet serial,
|
||||
// in unsigned long serialLength);
|
||||
NS_IMETHODIMP CertBlocklist::IsCertRevoked(const uint8_t* aIssuer, uint32_t aIssuerLength,
|
||||
const uint8_t* aSerial, uint32_t aSerialLength,
|
||||
bool* _retval)
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
|
||||
mozilla::pkix::Input issuer;
|
||||
mozilla::pkix::Input serial;
|
||||
if (issuer.Init(aIssuer, aIssuerLength) != mozilla::pkix::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (serial.Init(aSerial, aSerialLength) != mozilla::pkix::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CertBlocklistItem item(issuer, serial);
|
||||
|
||||
*_retval = mBlocklist.Contains(item);
|
||||
|
||||
return NS_OK;
|
||||
}
|
71
security/manager/boot/src/CertBlocklist.h
Normal file
71
security/manager/boot/src/CertBlocklist.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- 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 "nsClassHashtable.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsICertBlocklist.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsIX509CertDB.h"
|
||||
#include "pkix/Input.h"
|
||||
|
||||
#define NS_CERT_BLOCKLIST_CID \
|
||||
{0x11aefd53, 0x2fbb, 0x4c92, {0xa0, 0xc1, 0x05, 0x32, 0x12, 0xae, 0x42, 0xd0} }
|
||||
|
||||
enum CertBlocklistItemState {
|
||||
CertNewFromBlocklist,
|
||||
CertOldFromLocalCache
|
||||
};
|
||||
|
||||
class CertBlocklistItem
|
||||
{
|
||||
public:
|
||||
CertBlocklistItem(mozilla::pkix::Input aIssuer, mozilla::pkix::Input aSerial);
|
||||
CertBlocklistItem(const CertBlocklistItem& aItem);
|
||||
~CertBlocklistItem();
|
||||
nsresult ToBase64(nsACString& b64IssuerOut, nsACString& b64SerialOut);
|
||||
bool operator==(const CertBlocklistItem& aItem) const;
|
||||
uint32_t Hash() const;
|
||||
bool mIsCurrent = false;
|
||||
|
||||
private:
|
||||
mozilla::pkix::Input mIssuer;
|
||||
uint8_t* mIssuerData;
|
||||
mozilla::pkix::Input mSerial;
|
||||
uint8_t* mSerialData;
|
||||
};
|
||||
|
||||
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 char* aIssuer,
|
||||
const char* aSerial,
|
||||
CertBlocklistItemState aItemState,
|
||||
mozilla::MutexAutoLock& /*proofOfLock*/);
|
||||
mozilla::Mutex mMutex;
|
||||
bool mModified = false;
|
||||
nsCOMPtr<nsIFile> mBackingFile;
|
||||
|
||||
protected:
|
||||
virtual ~CertBlocklist();
|
||||
};
|
||||
|
||||
#endif // CertBlocklist_h
|
@ -9,6 +9,7 @@ EXPORTS.mozilla += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CertBlocklist.cpp',
|
||||
'DataStorage.cpp',
|
||||
'nsBOOTModule.cpp',
|
||||
'nsEntropyCollector.cpp',
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
|
||||
#include "CertBlocklist.h"
|
||||
#include "nsEntropyCollector.h"
|
||||
#include "nsSecureBrowserUIImpl.h"
|
||||
#include "nsSecurityWarningDialogs.h"
|
||||
@ -12,6 +13,7 @@
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEntropyCollector)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecureBrowserUIImpl)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(CertBlocklist, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSecurityWarningDialogs, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSiteSecurityService, Init)
|
||||
|
||||
@ -19,12 +21,14 @@ NS_DEFINE_NAMED_CID(NS_ENTROPYCOLLECTOR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SECURITYWARNINGDIALOGS_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SECURE_BROWSER_UI_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SITE_SECURITY_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_CERT_BLOCKLIST_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kBOOTCIDs[] = {
|
||||
{ &kNS_ENTROPYCOLLECTOR_CID, false, nullptr, nsEntropyCollectorConstructor },
|
||||
{ &kNS_SECURITYWARNINGDIALOGS_CID, false, nullptr, nsSecurityWarningDialogsConstructor },
|
||||
{ &kNS_SECURE_BROWSER_UI_CID, false, nullptr, nsSecureBrowserUIImplConstructor },
|
||||
{ &kNS_SITE_SECURITY_SERVICE_CID, false, nullptr, nsSiteSecurityServiceConstructor },
|
||||
{ &kNS_CERT_BLOCKLIST_CID, false, nullptr, CertBlocklistConstructor},
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
@ -33,6 +37,7 @@ static const mozilla::Module::ContractIDEntry kBOOTContracts[] = {
|
||||
{ NS_SECURITYWARNINGDIALOGS_CONTRACTID, &kNS_SECURITYWARNINGDIALOGS_CID },
|
||||
{ NS_SECURE_BROWSER_UI_CONTRACTID, &kNS_SECURE_BROWSER_UI_CID },
|
||||
{ NS_SSSERVICE_CONTRACTID, &kNS_SITE_SECURITY_SERVICE_CID },
|
||||
{ NS_CERTBLOCKLIST_CONTRACTID, &kNS_CERT_BLOCKLIST_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
@ -7,15 +7,16 @@
|
||||
#include "nsNSSComponent.h"
|
||||
|
||||
#include "ExtendedValidation.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsCertVerificationThread.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsCertVerificationThread.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsICertBlocklist.h"
|
||||
#include "nsICertOverrideService.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/PublicSSL.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
@ -1071,6 +1072,12 @@ nsNSSComponent::InitializeNSS()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// ensure the CertBlocklist is initialised
|
||||
nsCOMPtr<nsICertBlocklist> certList = do_GetService(NS_CERTBLOCKLIST_CONTRACTID);
|
||||
if (!certList) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// dynamic options from prefs
|
||||
setValidationOptions(true, lock);
|
||||
|
||||
|
264
security/manager/ssl/tests/unit/test_cert_blocklist.js
Normal file
264
security/manager/ssl/tests/unit/test_cert_blocklist.js
Normal file
@ -0,0 +1,264 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
// This test checks a number of things:
|
||||
// * it ensures that data loaded from revocations.txt on startup is present
|
||||
// * it ensures that certItems in blocklist.xml are persisted correctly
|
||||
// * it ensures that items in the CertBlocklist are seen as revoked by the
|
||||
// cert verifier
|
||||
// * it does a sanity check to ensure other cert verifier behavior is
|
||||
// unmodified
|
||||
|
||||
let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
|
||||
// First, we need to setup appInfo for the blocklist service to work
|
||||
let id = "xpcshell@tests.mozilla.org";
|
||||
let appName = "XPCShell";
|
||||
let version = "1";
|
||||
let platformVersion = "1.9.2";
|
||||
let appInfo = {
|
||||
// nsIXULAppInfo
|
||||
vendor: "Mozilla",
|
||||
name: appName,
|
||||
ID: id,
|
||||
version: version,
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: platformVersion ? platformVersion : "1.0",
|
||||
platformBuildID: "2007010101",
|
||||
|
||||
// nsIXULRuntime
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
invalidateCachesOnRestart: function invalidateCachesOnRestart() {
|
||||
// Do nothing
|
||||
},
|
||||
|
||||
// nsICrashReporter
|
||||
annotations: {},
|
||||
|
||||
annotateCrashReport: function(key, data) {
|
||||
this.annotations[key] = data;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
Ci.nsICrashReporter,
|
||||
Ci.nsISupports])
|
||||
};
|
||||
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
appInfo.QueryInterface(iid);
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return appInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
|
||||
const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
|
||||
registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
|
||||
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
|
||||
|
||||
// we need to ensure we setup revocation data before certDB, or we'll start with
|
||||
// no revocation.txt in the profile
|
||||
let profile = do_get_profile();
|
||||
let revocations = profile.clone();
|
||||
revocations.append("revocations.txt");
|
||||
if (!revocations.exists()) {
|
||||
let existing = do_get_file("test_onecrl/sample_revocations.txt", false);
|
||||
existing.copyTo(profile,"revocations.txt");
|
||||
}
|
||||
|
||||
let certDB = Cc["@mozilla.org/security/x509certdb;1"]
|
||||
.getService(Ci.nsIX509CertDB);
|
||||
|
||||
// set up a test server to serve the blocklist.xml
|
||||
let testserver = new HttpServer();
|
||||
|
||||
let blocklist_contents =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
|
||||
"<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
|
||||
// test with some bad data ...
|
||||
"<certItems><certItem issuerName='Some nonsense in issuer'>" +
|
||||
"<serialNumber>AkHVNA==</serialNumber>" +
|
||||
"</certItem><certItem issuerName='MA0xCzAJBgNVBAMMAmNh'>" +
|
||||
"<serialNumber>some nonsense in serial</serialNumber>" +
|
||||
"</certItem><certItem issuerName='some nonsense in both issuer'>" +
|
||||
"<serialNumber>and serial</serialNumber></certItem>" +
|
||||
// some mixed
|
||||
// In this case, the issuer name and the valid serialNumber correspond
|
||||
// to test-int.der in tlsserver/
|
||||
"<certItem issuerName='MBIxEDAOBgNVBAMTB1Rlc3QgQ0E='>" +
|
||||
"<serialNumber>oops! more nonsense.</serialNumber>" +
|
||||
"<serialNumber>BA==</serialNumber></certItem>" +
|
||||
// ... and some good
|
||||
// This item corresponds to an entry in sample_revocations.txt where:
|
||||
// isser name is "another imaginary issuer" base-64 encoded, and
|
||||
// serialNumbers are:
|
||||
// "serial2." base-64 encoded, and
|
||||
// "another serial." base-64 encoded
|
||||
// We need this to ensure that existing items are retained if they're
|
||||
// also in the blocklist
|
||||
"<certItem issuerName='YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy'>" +
|
||||
"<serialNumber>c2VyaWFsMi4=</serialNumber>" +
|
||||
"<serialNumber>YW5vdGhlciBzZXJpYWwu</serialNumber>" +
|
||||
"</certItem></certItems></blocklist>";
|
||||
testserver.registerPathHandler("/push_blocked_cert/",
|
||||
function serveResponse(request, response) {
|
||||
response.write(blocklist_contents);
|
||||
});
|
||||
|
||||
// start the test server
|
||||
testserver.start(-1);
|
||||
let port = testserver.identity.primaryPort;
|
||||
|
||||
// Setup the addonManager
|
||||
let addonManager = Cc["@mozilla.org/addons/integration;1"]
|
||||
.getService(Ci.nsIObserver)
|
||||
.QueryInterface(Ci.nsITimerCallback);
|
||||
addonManager.observe(null, "addons-startup", null);
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
function verify_cert(file, expectedError) {
|
||||
let cert_der = readFile(do_get_file(file));
|
||||
let ee = certDB.constructX509(cert_der, cert_der.length);
|
||||
equal(expectedError, certDB.verifyCertNow(ee, certificateUsageSSLServer,
|
||||
NO_FLAGS, {}, {}));
|
||||
}
|
||||
|
||||
function load_cert(cert, trust) {
|
||||
let file = "tlsserver/" + cert + ".der";
|
||||
addCertFromFile(certDB, file, trust);
|
||||
}
|
||||
|
||||
function testIsRevoked(certList, issuerString, serialString) {
|
||||
let issuer = converter.convertToByteArray(issuerString, {});
|
||||
let serial = converter.convertToByteArray(serialString, {});
|
||||
return certList.isCertRevoked(issuer,
|
||||
issuerString.length,
|
||||
serial,
|
||||
serialString.length);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
// import the certificates we need
|
||||
load_cert("test-ca", "CTu,CTu,CTu");
|
||||
load_cert("test-int", ",,");
|
||||
|
||||
let certList = Cc["@mozilla.org/security/certblocklist;1"]
|
||||
.getService(Ci.nsICertBlocklist);
|
||||
|
||||
// check some existing items in revocations.txt are blocked. Since the
|
||||
// CertBlocklistItems don't know about the data they contain, we can use
|
||||
// arbitrary data (not necessarily DER) to test if items are revoked or not.
|
||||
// This test corresponds to:
|
||||
// issuer: c29tZSBpbWFnaW5hcnkgaXNzdWVy
|
||||
// serial: c2VyaWFsLg==
|
||||
ok(testIsRevoked(certList, "some imaginary issuer","serial."),
|
||||
"issuer / serial pair should be blocked");
|
||||
|
||||
// And this test corresponds to:
|
||||
// issuer: YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy
|
||||
// serial: c2VyaWFsMi4=
|
||||
ok(testIsRevoked(certList, "another imaginary issuer","serial2."),
|
||||
"issuer / serial pair should be blocked");
|
||||
|
||||
// Soon we'll load a blocklist which revokes test-int.der, which issued
|
||||
// test-int-ee.der.
|
||||
// Check the cert validates before we load the blocklist
|
||||
let file = "tlsserver/test-int-ee.der";
|
||||
verify_cert(file, Cr.NS_OK);
|
||||
|
||||
// blocklist load is async so we must use add_test from here
|
||||
add_test(function() {
|
||||
let certblockObserver = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
run_next_test();
|
||||
Services.obs.removeObserver(this, "blocklist-updated");
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(certblockObserver, "blocklist-updated", false);
|
||||
Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
|
||||
port + "/push_blocked_cert/");
|
||||
let blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
|
||||
.getService(Ci.nsITimerCallback);
|
||||
blocklist.notify(null);
|
||||
});
|
||||
|
||||
add_test(function() {
|
||||
// The blocklist will be loaded now. Let's check the data is sane.
|
||||
// In particular, we should still have the revoked issuer / serial pair
|
||||
// that was in both revocations.txt and the blocklist.xml
|
||||
ok(testIsRevoked(certList, "another imaginary issuer", "serial2."),
|
||||
"issuer / serial pair should be blocked");
|
||||
|
||||
// Check that both serials in the certItem with multiple serials were read
|
||||
// properly
|
||||
ok(testIsRevoked(certList, "another imaginary issuer", "serial2."),
|
||||
"issuer / serial pair should be blocked");
|
||||
ok(testIsRevoked(certList, "another imaginary issuer", "another serial."),
|
||||
"issuer / serial pair should be blocked");
|
||||
|
||||
// Check the blocklist entry has been persisted properly to the backing
|
||||
// file
|
||||
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 contents = "";
|
||||
let hasmore = false;
|
||||
do {
|
||||
var line = {};
|
||||
hasmore = inputStream.readLine(line);
|
||||
contents = contents + (contents.length == 0 ? "" : "\n") + line.value;
|
||||
} while (hasmore);
|
||||
let expected = "# Auto generated contents. Do not edit.\n" +
|
||||
"MBIxEDAOBgNVBAMTB1Rlc3QgQ0E=\n" +
|
||||
" BA==\n" +
|
||||
"YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy\n" +
|
||||
" YW5vdGhlciBzZXJpYWwu\n" +
|
||||
" c2VyaWFsMi4=";
|
||||
equal(contents, expected, "revocations.txt should be as expected");
|
||||
|
||||
// Check the blocklisted intermediate now causes a failure
|
||||
let file = "tlsserver/test-int-ee.der";
|
||||
verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
|
||||
|
||||
// Check a non-blocklisted chain still validates OK
|
||||
file = "tlsserver/default-ee.der";
|
||||
verify_cert(file, Cr.NS_OK);
|
||||
|
||||
// Check a bad cert is still bad (unknown issuer)
|
||||
file = "tlsserver/unknown-issuer.der";
|
||||
verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER);
|
||||
|
||||
// check that save with no further update is a no-op
|
||||
let lastModified = revocations.lastModifiedTime;
|
||||
// add an already existing entry
|
||||
certList.addRevokedCert("YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy","c2VyaWFsMi4=");
|
||||
certList.saveEntries();
|
||||
let newModified = revocations.lastModifiedTime;
|
||||
equal(lastModified, newModified,
|
||||
"saveEntries with no modifications should not update the backing file");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// we need to start the async portions of the test
|
||||
run_next_test();
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
# a sample revocations.txt for tests
|
||||
# Lines starting with '#' are ignored - as are empty lines like this:
|
||||
|
||||
# otherwise:
|
||||
# non-empty lines are treated as base-64 encoded DER issuer data
|
||||
# ...unless the line starts with a ' ' (space) character, in which case it's
|
||||
# assumed to be base-64 encoded DER serial data.
|
||||
|
||||
# First a serial with no issuer to ensure this doesn't cause parsing to fail
|
||||
# (there should be an issuer first, but we need to test this won't fail)
|
||||
dGVzdA==
|
||||
# next, let's ensure data that isn't valid base64 doesn't cause breakage.
|
||||
this serial isn't valid base64 (but then there's no issuer anyway)
|
||||
Neither is this issuer, though the serial is fine
|
||||
dGVzdA==
|
||||
dGVzdA==
|
||||
in this case, issuer is fine but not the serial
|
||||
# Next two entries; we can add valid base-64 encoded data for some basic tests:
|
||||
# issuer is "some imaginary issuer" base-64 encoded
|
||||
# and serial "serial." base-64 encoded
|
||||
c29tZSBpbWFnaW5hcnkgaXNzdWVy
|
||||
c2VyaWFsLg==
|
||||
# issuer is "another imaginary issuer" base-64 encoded
|
||||
# serial is "serial2." base-64 encoded
|
||||
YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy
|
||||
c2VyaWFsMi4=
|
Binary file not shown.
@ -267,6 +267,7 @@ export_cert localhostAndExampleCom default-ee.der
|
||||
make_EE ocspOtherEndEntity 'CN=Other Cert' testCA "localhost,*.example.com"
|
||||
|
||||
make_INT testINT 'CN=Test Intermediate' testCA
|
||||
export_cert testINT test-int.der
|
||||
make_EE ocspEEWithIntermediate 'CN=Test End-entity with Intermediate' testINT "localhost,*.example.com"
|
||||
make_EE expired 'CN=Expired Test End-entity' testCA "expired.example.com" "-w -400"
|
||||
export_cert expired expired-ee.der
|
||||
@ -279,6 +280,7 @@ make_EE selfsigned 'CN=Self-signed Test End-entity' testCA "selfsigned.example.c
|
||||
# get regenerated. Either way, deletedINT will then be removed again.
|
||||
make_INT deletedINT 'CN=Test Intermediate to delete' testCA
|
||||
make_EE unknownissuer 'CN=Test End-entity from unknown issuer' deletedINT "unknownissuer.example.com"
|
||||
export_cert unknownissuer unknown-issuer.der
|
||||
|
||||
$RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -D -n deletedINT
|
||||
|
||||
@ -320,4 +322,8 @@ make_V1 v1Cert 'CN=V1 Cert' testCA
|
||||
export_cert v1Cert v1Cert.der
|
||||
make_EE eeIssuedByV1Cert 'CN=EE Issued by V1 Cert' v1Cert "localhost,*.example.com"
|
||||
|
||||
# Make a valid EE using testINT to test OneCRL revocation of testINT
|
||||
make_EE eeIssuedByIntermediate 'CN=EE issued by intermediate' testINT "localhost"
|
||||
export_cert eeIssuedByIntermediate test-int-ee.der
|
||||
|
||||
cleanup
|
||||
|
Binary file not shown.
BIN
security/manager/ssl/tests/unit/tlsserver/test-int-ee.der
Normal file
BIN
security/manager/ssl/tests/unit/tlsserver/test-int-ee.der
Normal file
Binary file not shown.
BIN
security/manager/ssl/tests/unit/tlsserver/test-int.der
Normal file
BIN
security/manager/ssl/tests/unit/tlsserver/test-int.der
Normal file
Binary file not shown.
BIN
security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der
Normal file
BIN
security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der
Normal file
Binary file not shown.
@ -18,6 +18,7 @@ support-files =
|
||||
test_ocsp_fetch_method/**
|
||||
test_keysize/**
|
||||
test_pinning_dynamic/**
|
||||
test_onecrl/**
|
||||
|
||||
[test_datasignatureverifier.js]
|
||||
[test_hash_algorithms.js]
|
||||
@ -46,6 +47,7 @@ skip-if = buildapp == "b2g" && processor == "arm"
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
skip-if = os == "android"
|
||||
[test_cert_blocklist.js]
|
||||
[test_ocsp_stapling_expired.js]
|
||||
run-sequentially = hardcoded ports
|
||||
# Bug 1009158: this test times out on Android
|
||||
|
@ -74,6 +74,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gVersionChecker",
|
||||
"@mozilla.org/xpcom/version-comparator;1",
|
||||
"nsIVersionComparator");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gCertBlocklistService",
|
||||
"@mozilla.org/security/certblocklist;1",
|
||||
"nsICertBlocklist");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() {
|
||||
return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
|
||||
QueryInterface(Ci.nsIPrefBranch);
|
||||
@ -725,6 +729,13 @@ Blocklist.prototype = {
|
||||
# <match name="description" exp="1[.]2[.]3"/>
|
||||
# </pluginItem>
|
||||
# </pluginItems>
|
||||
# <certItems>
|
||||
# <!-- issuerName is the DER issuer name data base64 encoded... -->
|
||||
# <certItem issuerName="MA0xCzAJBgNVBAMMAmNh">
|
||||
# <!-- ... as is the serial number DER data -->
|
||||
# <serialNumber>AkHVNA==</serialNumber>
|
||||
# </certItem>
|
||||
# </certItems>
|
||||
# </blocklist>
|
||||
*/
|
||||
|
||||
@ -862,12 +873,17 @@ Blocklist.prototype = {
|
||||
this._pluginEntries = this._processItemNodes(element.childNodes, "plugin",
|
||||
this._handlePluginItemNode);
|
||||
break;
|
||||
case "certItems":
|
||||
this._processItemNodes(element.childNodes, "cert",
|
||||
this._handleCertItemNode.bind(this));
|
||||
break;
|
||||
default:
|
||||
Services.obs.notifyObservers(element,
|
||||
"blocklist-data-" + element.localName,
|
||||
null);
|
||||
}
|
||||
}
|
||||
gCertBlocklistService.saveEntries();
|
||||
}
|
||||
catch (e) {
|
||||
LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
|
||||
@ -889,6 +905,22 @@ Blocklist.prototype = {
|
||||
return result;
|
||||
},
|
||||
|
||||
_handleCertItemNode: function Blocklist_handleCertItemNode(blocklistElement,
|
||||
result) {
|
||||
let issuer = blocklistElement.getAttribute("issuerName");
|
||||
for (let snElement of blocklistElement.children) {
|
||||
try {
|
||||
if (issuer) {
|
||||
gCertBlocklistService.addRevokedCert(issuer, snElement.textContent);
|
||||
}
|
||||
} catch (e) {
|
||||
// we want to keep trying other elements since missing all items
|
||||
// is worse than missing one
|
||||
LOG("Blocklist::_handleCertItemNode: Error adding revoked cert " + e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleEmItemNode: function Blocklist_handleEmItemNode(blocklistElement, result) {
|
||||
if (!matchesOSABI(blocklistElement))
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user