Files
roytam1 b564bd67d3 import changes from `dev' branch of rmottola/Arctic-Fox:
- Use CompositorWidgetProxy for dispatching vsync to the compositor. (bug 1269037 part 1, r=mchang) (c8b7a4240c)
- Remove unused null widget checks. (bug 1269037 part 2, r=mchang) (4f4cc9952b)
- Bug 1269422: Wrap Gonk widget in |CompositorWidgetProxyWrapper|. r=dvander (4513035cbf)
- Remove nsBaseWidget::NewCompositorBridgeParent. (bug 1272472 part 1, r=kats) (ca813c1f2b)
- Use IPDL to schedule composites on GTK. (bug 1272472 part 2, r=nical) (7402cf834e)
- Use IPC to schedule composites on Windows. (bug 1272472 part 3, r=jimm) (e5d7281dd7)
- Don't use nsIWidget to check APZ in LayerManagerComposite. (bug 1269653 part 1, r=kats) (79a1644111)
- Restrict SetDispAcquireFence's nsIWidget access to Gonk. (bug 1269653 part 2, r=kats) (72110b7bc7)
- Bug 1264764 - Move PTexture under PCompositorBridge r=nical,dvander (17e6ec7fc1)
- Move CompositorThreadHolder into its own file. (bug 1273017 part 1, r=mattwoodrow) (464ede8be1)
- Move CompositorBridgeParent::CompositorLoop to CompositorThreadHolder. (bug 1273017 part 2, r=mattwoodrow) (85708f3cde)
- Bug 1268313: Part 1 - Be explicit about which NewRunnableMethod callers want to be able to cancel. r=froydnj (faa07aa139)
- Bug 1268313: Part 2 - Replace some NewRunnableMethods with NS_NewNonOwningRunnableMethod. r=froydnj (010c43d000)
- Bug 1268313: Part 3 - Replace some NewCancelableRunnableMethod with NS_NewNonOwningCancelableRunnableMethod. r=froydnj (55018ef234)
- Bug 1268313: Part 4 - Replace NewCancelableRunnableMethod with NS_NewCancelableRunnableMethod. r=froydnj (c22711b35d)
- Bug 1268313: Part 5 - Make NS_NewRunnableMethod able to call const functions. r=froydnj (b0f60963a3)
- Bug 1268313: Part 6 - Replace NewRunnableMethod with NS_NewRunnableMethod. r=froydnj (18d40def2c)
- Bug 1260950 - Set mInitialSizeFound to true when the initial size id found. r=jesup, r=pehrsons a=kwierso (5d6abe57e0)
- Bug 1237176 - Notify synth start if we get a finished event without a blocking-changed event. r=roc (c1aebe903a)
- Bug 911546, use a runnable so that popups don't rollup during a grab, r=karlt (a06bd44e6c)
- Bug 1237617 - Call nsWindow::ForcePresent during going active r=bas.schouten (066cad8f89)
- Bug 1268313: Part 7 - Move NS_NewRunnableMethod and friends to mozilla::NewRunnableMethod. r=froydnj (8b4bf34961)
- Bug 1266595: Followup to fix IPDL tests. r=billm (216f2dcff5)
- Bug 1268313: Fix up IPDL tests. r=billm (228348d642)
- Add WinCompositorWidgetProxy. (bug 1265975 part 1, r=jimm) (bfafe7a8e2)
- Implement WinCompositorWidgetProxy::GetClientSize. (bug 1265975 part 2, r=jimm) (a8710a3259)
- Move the WM_SETTEXT present lock to CompositorWidgetProxy. (bug 1265975 part 3, r=jimm) (297ce28c8a)
- Move transparency handling to WinCompositorWigetProxy. (bug 1265975 part 4, r=jimm) (46ba0c6d01)
- Remove Windows-specific compositor calls to nsIWidget. (bug 1265975 part 5, r=jimm) (3ef157c160)
- Remove plugin-related CompositorBridgeParent use of nsIWidget. (bug 1265975 part 6, r=jimm) (6d80cdd6fd)
- Hide top-level CompositorBridgeParents behind a new API. (bug 1272472 part 4, r=mattwoodrow,kats,gwagner) (228c0efdb7)
- Bug 1253424 - part 1 - add a already_AddRefed nsTransactionStack::Push overload; r=erahm (7059e20914)
- Bug 1253424 - part 2 - add nsTransactionStack::IsEmpty; r=erahm (7e9764a146)
- Bug 1254618 - modify nsTransactionStack to use nsDeque rather than std::deque; r=ehsan (5e47ea431e)
- Bug 1136857 - Make DOMStorageCache::mLoaded flag atomic to prevent potential races, r=nfroyd (39aaea1de3)
- Bug 1265408 - Add webidl for IIRFilterNode; r=smaug (040ce9aa43)
- Bug 1265408 - Implement IIRFilterNode; r=padenot (6bf569a412)
- Bug 1265408 - Import IIRFilter from blink; r=padenot (71b28c0ad2)
- Bug 1265408 - Use IIRFilter from blink; r=padenot (5d058d8568)
- Bug 1265408 - Add buffersAreZero to IIRFilter; r=karlt (45edba3e13)
- Bug 1265408 - Avoid subnormals in IIRFilter; r=karlt (0e1ae93f0b)
- Bug 1265408 - Add LogToDeveloperConsole to WebAudioUtils; r=padenot (88d5f0222a)
- Bug 1268984 - Store GMPStorage on GMPServiceParent so that it persists inside the same PB session. r=gerald (17d4d0abaf)
- Bug 1267905 - Replace uses of ScopedCERTCertList with UniqueCERTCertList. r=keeler (783bf11b2a)
- Bug 1270005 - Replace uses of ScopedPK11SlotInfo with UniquePK11SlotInfo in PSM. r=keeler (ea9a4011aa)
- Bug 1271501 - Remove unnecessary uses of reinterpret_cast in PSM. r=keeler (6be40f0a85)
- Bug 1271501 - Downgrade unnecessarily strong reinterpret_casts in PSM. r=keeler (95245f00ce)
- Bug 1082346 - 01. Convert PKCS12 password endian using copyAndSwapToBigEndian. r=keeler (9cc58fc550)
- Bug 1082346 - 02. Test case. r=keeler r=Cykesiopka (7fb0e8abc4)
- Bug 160122 - Stop using PR_smprintf in PSM. r=keeler (1e5b68819c)
- Bug 1271501 - Use mozilla::BitwiseCast instead of reinterpret_cast in PSM. r=keeler (894966a2ef)
- Bug 1273855: TraceLogger - Include PID in the log names in order to support browser with e10s, r=bbouvier (8cf2233db3)
- Bug 1274189. Part 1 - rename some functions to be consistent with other MediaDataDecoder sub-classes. r=jya. (4511b3d3f7)
- Bug 1274189. Part 2 - remove use of FlushableTaskQueue::Flush(). r=jya. (77e745fdd1)
- Bug 1274189. Part 3 - remove use of FlushableTaskQueue. r=jya (aac61dcd02)
- Bug 1269963. Part 1 - Add a SyncRunnable::DispatchToThread() overload for AbstractThread. r=bobbyholley. (839752aff4)
- Bug 1269672 - part1 : revert sampling rate changing of the bug1235612. (9015782e13)
- Bug 1270698 - check if we need to enter buffering periodically to ensure we start buffering when running out of decoded audio/video data. r=cpearce. (16734549b7)
- Bug 1271581 - use newCurrentTime, instead of GetMediaTime() to decide the nextState; r=jwwang (9c5075eada)
- Bug 1224973 - Part 1: Remove MediaDecoderOwner->IsHidden(). r=cpearce,jwwang (4fde3ede5a)
- Bug 1224973 - Part 2: Set MediaDecoder visibility via NotifyOwnerActivityChanged. r=cpearce,jwwang (be917202eb)
- Bug 1224973 - Part 3: Plumb element visibility into MDSM. r=jya,jwwang (9ec83fa243)
- Bug 1224973 - Part 4: Pref media.suspend-bkgnd-video.enabled. r=cpearce,jwwang (43413a025f)
- Bug 1269408: P1. Retry InternalSeek if previous attempt failed once more data is available. r=gerald (05db58dc7c)
- crude fix (0097068989)
- Bug 1269408: P2. Update mochitest. r=gerald (464b4c0724)
- Bug 1269408: P3. Ensure a new seek request will cancel the previous internal seek. r=gerald (6ed4b8dc95)
- Bug 1269408: P4. Ensure the decoders are flushed prior performing an internal seek. r=gerald (074234067b)
- Bug 1269408: P5. Only drop the seek target if it's exactly the seek target. r=gerald (88701eb05a)
- Bug 1269408: P6. Add debugging information, useful when a mochitest timeout. r=gerald (ef0270ab0d)
- Bug 1269408: P7. Start skip to next keyframe logic when resume point is behind current time. r=gerald (bd40ebf3bc)
- Bug 1269408: P8. Add debugging log. r=gerald (e6dbd1f0a6)
- Bug 1269408: P9. Move handling logic of skip to next keyframe to its own function. r=gerald (3c8039e417)
- Bug 1269408: P10. Reject promise early if in error state. r=me (8af54c574e)
- Bug 1224973 - Part 5: Implement suspend decoding for background video. r=cpearce,jwwang,jya (22081521e3)
- Bug 1242874 - part1 : create suspened types. r=baku (d3ac9548e5)
- Bug 1242874 - part2 : window's suspend attribute. r=baku, r=ehsan (1fd9dc2647)
- remove allowscirpted (39ab523036)
- Bug 1242874 - part3 : implement different suspended methods. r=baku, r=jwwang (25d1f27a03)
- Bug 1242874 - part4 : wrap the volume/mute/suspend for notifyStartedPlaying. r=baku (b8ba3238c2)
- bug 1242874 - part5 : add test. r=baku, r=ehsan (f840139b5a)
- Bug 1235612 - Part 1: Implement notify media-playback. r=baku (b5ec29da20)
- Bug 1235612 - Part 2: Notify audible state in NotifyStartedPlaying. r=baku (dc38583a62)
- Bug 1235612 - Part 3: Implement the logic of audible state notification for agent owners. r=baku (f65b3952fa)
- Bug 1235612 - Part 4: Modify check audible method. r=jwwang (73457e39eb)
- Bug 1269672 - part2 : move audible data checking from MDSM to DecodedAudioDataSink. (d2c3b6874c)
- Bug 1269936 - Introduce and call a runtime-wide servo initialization hook. r=heycam (d4d505d4c2)
- Bug 1263778 - Rename a bunch of low-level [[Prototype]] access methods to make their interactions with statically-known and dynamically-computed [[Prototype]]s clearer. r=efaust (66bbe8e7db)
- Bug 888969 - Permit a cyclic [[Prototype]] chain to be created through a Location object. r=bz, r=efaust (3e3b9cbb16)
- re-apply Bug 1054906 - Implement ES6 Symbol.hasInstance 2/2; r=jandem (8d5c7573ff)
- Bug 1054906 - Implement ES6 Symbol.hasInstance 1/2; r=evilpie,bz (a836904e5d)
- fix misspatch (54a5f2d708)
- Bug 1270349 part 1. Add IDL parser support for [LegacyUnenumerableNamedProperties]. r=peterv (8c836bc74a)
- Bug 1270349 part 2. Add [LegacyUnenumerableNamedProperties] to the interfaces that specify it in DOM and HTML. r=peterv (25d3cc1377)
- Bug 1270349 part 3. Add a way to ask an interface descriptor for a proxy whether its named props should be enumerable. r=peterv (0a9f804867)
- Bug 1270349 part 4. Use LegacyUnenumerableNamedProperties instead of NameIsEnumerable() calls to determine whether named props on DOM proxies should be enumerable. r=peterv (82f5158963)
- Bug 1270349 part 5. Use LegacyUnenumerableNamedProperties instead of passing flags to GetSupportedNames to determine whether named props on DOM proxies should be reflected in ownPropertyKeys. r=peterv (3984176834)
- Bug 1270349 followup to address a review comment. r=peterv (b49f4c5335)
2024-10-03 21:54:00 +08:00

671 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/Casting.h"
#include "mozilla/Logging.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 "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 LazyLogModule gCertBlockPRLog("CertBlock");
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(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_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(
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.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(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),
BitwiseCast<const uint8_t*, const char*>(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)
{
auto blocklist = static_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);
}
}