mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
Bug 1835298: Encrypt CacheAPI data on disk in PBM.r=dom-storage-reviewers,janv
Depends on D180296 Differential Revision: https://phabricator.services.mozilla.com/D180297
This commit is contained in:
parent
0890b51845
commit
a68c2ad072
3
dom/cache/Action.h
vendored
3
dom/cache/Action.h
vendored
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_cache_Action_h
|
||||
#define mozilla_dom_cache_Action_h
|
||||
|
||||
#include "CacheCipherKeyManager.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/dom/cache/Types.h"
|
||||
#include "mozilla/dom/SafeRefPtr.h"
|
||||
@ -56,7 +57,7 @@ class Action : public SafeRefCounted<Action> {
|
||||
virtual void RunOnTarget(
|
||||
SafeRefPtr<Resolver> aResolver,
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
Data* aOptionalData) = 0;
|
||||
Data* aOptionalData, const Maybe<CipherKey>& aMaybeCipherKey) = 0;
|
||||
|
||||
// Called on initiating thread when the Action is canceled. The Action is
|
||||
// responsible for calling Resolver::Resolve() as normal; either with a
|
||||
|
21
dom/cache/CacheCipherKeyManager.h
vendored
Normal file
21
dom/cache/CacheCipherKeyManager.h
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
#ifndef DOM_CACHE_CACHECIPHERKEYMANAGER_H_
|
||||
#define DOM_CACHE_CACHECIPHERKEYMANAGER_H_
|
||||
|
||||
#include "mozilla/dom/quota/CipherKeyManager.h"
|
||||
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
|
||||
|
||||
namespace mozilla::dom::cache {
|
||||
|
||||
using CipherStrategy = mozilla::dom::quota::IPCStreamCipherStrategy;
|
||||
using CipherKeyManager = mozilla::dom::quota::CipherKeyManager<CipherStrategy>;
|
||||
using CipherKey = CipherStrategy::KeyType;
|
||||
|
||||
} // namespace mozilla::dom::cache
|
||||
|
||||
#endif // DOM_CACHE_CACHECIPHERKEYMANAGER_H_
|
73
dom/cache/Context.cpp
vendored
73
dom/cache/Context.cpp
vendored
@ -5,7 +5,6 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
|
||||
#include "CacheCommon.h"
|
||||
|
||||
#include "mozilla/AutoRestore.h"
|
||||
@ -19,11 +18,13 @@
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "QuotaClientImpl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -35,8 +36,9 @@ class NullAction final : public Action {
|
||||
NullAction() = default;
|
||||
|
||||
virtual void RunOnTarget(mozilla::SafeRefPtr<Resolver> aResolver,
|
||||
const mozilla::Maybe<CacheDirectoryMetadata>&,
|
||||
Data*) override {
|
||||
const mozilla::Maybe<CacheDirectoryMetadata>&, Data*,
|
||||
const mozilla::Maybe<mozilla::dom::cache::CipherKey>&
|
||||
/* aMaybeCipherKey */) override {
|
||||
// Resolve success immediately. This Action does no actual work.
|
||||
MOZ_DIAGNOSTIC_ASSERT(aResolver);
|
||||
aResolver->Resolve(NS_OK);
|
||||
@ -216,6 +218,7 @@ class Context::QuotaInitRunnable final : public nsIRunnable {
|
||||
Maybe<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
|
||||
Maybe<CacheDirectoryMetadata> mDirectoryMetadata;
|
||||
RefPtr<DirectoryLock> mDirectoryLock;
|
||||
RefPtr<CipherKeyManager> mCipherKeyManager;
|
||||
State mState;
|
||||
Atomic<bool> mCanceled;
|
||||
|
||||
@ -399,11 +402,18 @@ Context::QuotaInitRunnable::Run() {
|
||||
QM_TRY(
|
||||
MOZ_TO_RESULT(quotaManager->EnsureTemporaryStorageIsInitialized()));
|
||||
|
||||
QM_TRY_UNWRAP(mDirectoryMetadata->mDir,
|
||||
quotaManager
|
||||
->EnsureTemporaryOriginIsInitialized(
|
||||
PERSISTENCE_TYPE_DEFAULT, *mDirectoryMetadata)
|
||||
.map([](const auto& res) { return res.first; }));
|
||||
QM_TRY_UNWRAP(
|
||||
mDirectoryMetadata->mDir,
|
||||
quotaManager
|
||||
->EnsureTemporaryOriginIsInitialized(
|
||||
mDirectoryMetadata->mPersistenceType, *mDirectoryMetadata)
|
||||
.map([](const auto& res) { return res.first; }));
|
||||
|
||||
auto* cacheQuotaClient = CacheQuotaClient::Get();
|
||||
MOZ_DIAGNOSTIC_ASSERT(cacheQuotaClient);
|
||||
|
||||
mCipherKeyManager =
|
||||
cacheQuotaClient->GetOrCreateCipherKeyManager(*mDirectoryMetadata);
|
||||
|
||||
mState = STATE_RUN_ON_TARGET;
|
||||
|
||||
@ -427,7 +437,11 @@ Context::QuotaInitRunnable::Run() {
|
||||
|
||||
// Execute the provided initialization Action. The Action must Resolve()
|
||||
// before returning.
|
||||
mInitAction->RunOnTarget(resolver.clonePtr(), mDirectoryMetadata, mData);
|
||||
|
||||
mInitAction->RunOnTarget(
|
||||
resolver.clonePtr(), mDirectoryMetadata, mData,
|
||||
mCipherKeyManager ? Some(mCipherKeyManager->Ensure()) : Nothing{});
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(resolver->Resolved());
|
||||
|
||||
mData = nullptr;
|
||||
@ -445,8 +459,11 @@ Context::QuotaInitRunnable::Run() {
|
||||
case STATE_COMPLETING: {
|
||||
NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
|
||||
mInitAction->CompleteOnInitiatingThread(mResult);
|
||||
|
||||
mContext->OnQuotaInit(mResult, mDirectoryMetadata,
|
||||
mDirectoryLock.forget());
|
||||
mDirectoryLock.forget(),
|
||||
mCipherKeyManager.forget());
|
||||
|
||||
mState = STATE_COMPLETE;
|
||||
|
||||
// Explicitly cleanup here as the destructor could fire on any of
|
||||
@ -477,12 +494,14 @@ class Context::ActionRunnable final : public nsIRunnable,
|
||||
public:
|
||||
ActionRunnable(SafeRefPtr<Context> aContext, Data* aData,
|
||||
nsISerialEventTarget* aTarget, SafeRefPtr<Action> aAction,
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata)
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
RefPtr<CipherKeyManager> aCipherKeyManager)
|
||||
: mContext(std::move(aContext)),
|
||||
mData(aData),
|
||||
mTarget(aTarget),
|
||||
mAction(std::move(aAction)),
|
||||
mDirectoryMetadata(aDirectoryMetadata),
|
||||
mCipherKeyManager(std::move(aCipherKeyManager)),
|
||||
mInitiatingThread(GetCurrentSerialEventTarget()),
|
||||
mState(STATE_INIT),
|
||||
mResult(NS_OK),
|
||||
@ -572,6 +591,7 @@ class Context::ActionRunnable final : public nsIRunnable,
|
||||
nsCOMPtr<nsISerialEventTarget> mTarget;
|
||||
SafeRefPtr<Action> mAction;
|
||||
const Maybe<CacheDirectoryMetadata> mDirectoryMetadata;
|
||||
RefPtr<CipherKeyManager> mCipherKeyManager;
|
||||
nsCOMPtr<nsIEventTarget> mInitiatingThread;
|
||||
State mState;
|
||||
nsresult mResult;
|
||||
@ -633,7 +653,9 @@ Context::ActionRunnable::Run() {
|
||||
mExecutingRunOnTarget = true;
|
||||
|
||||
mState = STATE_RUNNING;
|
||||
mAction->RunOnTarget(SafeRefPtrFromThis(), mDirectoryMetadata, mData);
|
||||
mAction->RunOnTarget(
|
||||
SafeRefPtrFromThis(), mDirectoryMetadata, mData,
|
||||
mCipherKeyManager ? Some(mCipherKeyManager->Ensure()) : Nothing{});
|
||||
|
||||
mData = nullptr;
|
||||
|
||||
@ -835,6 +857,19 @@ Maybe<DirectoryLock&> Context::MaybeDirectoryLockRef() const {
|
||||
return ToMaybeRef(mDirectoryLock.get());
|
||||
}
|
||||
|
||||
CipherKeyManager& Context::MutableCipherKeyManagerRef() {
|
||||
MOZ_ASSERT(mTarget->IsOnCurrentThread());
|
||||
MOZ_DIAGNOSTIC_ASSERT(mCipherKeyManager);
|
||||
|
||||
return *mCipherKeyManager;
|
||||
}
|
||||
|
||||
const Maybe<CacheDirectoryMetadata>& Context::MaybeCacheDirectoryMetadataRef()
|
||||
const {
|
||||
MOZ_ASSERT(mTarget->IsOnCurrentThread());
|
||||
return mDirectoryMetadata;
|
||||
}
|
||||
|
||||
void Context::CancelAll() {
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
|
||||
@ -961,9 +996,9 @@ void Context::Start() {
|
||||
void Context::DispatchAction(SafeRefPtr<Action> aAction, bool aDoomData) {
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
|
||||
auto runnable =
|
||||
MakeSafeRefPtr<ActionRunnable>(SafeRefPtrFromThis(), mData, mTarget,
|
||||
std::move(aAction), mDirectoryMetadata);
|
||||
auto runnable = MakeSafeRefPtr<ActionRunnable>(
|
||||
SafeRefPtrFromThis(), mData, mTarget, std::move(aAction),
|
||||
mDirectoryMetadata, mCipherKeyManager);
|
||||
|
||||
if (aDoomData) {
|
||||
mData = nullptr;
|
||||
@ -980,7 +1015,8 @@ void Context::DispatchAction(SafeRefPtr<Action> aAction, bool aDoomData) {
|
||||
|
||||
void Context::OnQuotaInit(
|
||||
nsresult aRv, const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
already_AddRefed<DirectoryLock> aDirectoryLock) {
|
||||
already_AddRefed<DirectoryLock> aDirectoryLock,
|
||||
already_AddRefed<CipherKeyManager> aCipherKeyManager) {
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInitRunnable);
|
||||
@ -988,6 +1024,11 @@ void Context::OnQuotaInit(
|
||||
|
||||
if (aDirectoryMetadata) {
|
||||
mDirectoryMetadata.emplace(*aDirectoryMetadata);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mCipherKeyManager);
|
||||
mCipherKeyManager = aCipherKeyManager;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT_IF(mDirectoryMetadata->mIsPrivate, mCipherKeyManager);
|
||||
}
|
||||
|
||||
// Always save the directory lock to ensure QuotaManager does not shutdown
|
||||
|
9
dom/cache/Context.h
vendored
9
dom/cache/Context.h
vendored
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_cache_Context_h
|
||||
#define mozilla_dom_cache_Context_h
|
||||
|
||||
#include "CacheCipherKeyManager.h"
|
||||
#include "mozilla/dom/SafeRefPtr.h"
|
||||
#include "mozilla/dom/cache/Types.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -125,6 +126,10 @@ class Context final : public SafeRefCounted<Context> {
|
||||
|
||||
Maybe<DirectoryLock&> MaybeDirectoryLockRef() const;
|
||||
|
||||
CipherKeyManager& MutableCipherKeyManagerRef();
|
||||
|
||||
const Maybe<CacheDirectoryMetadata>& MaybeCacheDirectoryMetadataRef() const;
|
||||
|
||||
// Cancel any Actions running or waiting to run. This should allow the
|
||||
// Context to be released and Listener::RemoveContext() will be called
|
||||
// when complete.
|
||||
@ -178,7 +183,8 @@ class Context final : public SafeRefCounted<Context> {
|
||||
void DispatchAction(SafeRefPtr<Action> aAction, bool aDoomData = false);
|
||||
void OnQuotaInit(nsresult aRv,
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
already_AddRefed<DirectoryLock> aDirectoryLock);
|
||||
already_AddRefed<DirectoryLock> aDirectoryLock,
|
||||
already_AddRefed<CipherKeyManager> aCipherKeyManager);
|
||||
|
||||
SafeRefPtr<ThreadsafeHandle> CreateThreadsafeHandle();
|
||||
|
||||
@ -206,6 +212,7 @@ class Context final : public SafeRefCounted<Context> {
|
||||
SafeRefPtr<ThreadsafeHandle> mThreadsafeHandle;
|
||||
|
||||
RefPtr<DirectoryLock> mDirectoryLock;
|
||||
RefPtr<CipherKeyManager> mCipherKeyManager;
|
||||
SafeRefPtr<Context> mNextContext;
|
||||
|
||||
public:
|
||||
|
41
dom/cache/DBAction.cpp
vendored
41
dom/cache/DBAction.cpp
vendored
@ -6,11 +6,11 @@
|
||||
|
||||
#include "mozilla/dom/cache/DBAction.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/cache/Connection.h"
|
||||
#include "mozilla/dom/cache/DBSchema.h"
|
||||
#include "mozilla/dom/cache/FileUtils.h"
|
||||
#include "mozilla/dom/cache/QuotaClient.h"
|
||||
#include "mozilla/dom/quota/Assertions.h"
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
#include "mozilla/net/nsFileProtocolHandler.h"
|
||||
@ -21,16 +21,11 @@
|
||||
#include "nsIURI.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsIFileURL.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla::dom::cache {
|
||||
|
||||
using mozilla::dom::quota::AssertIsOnIOThread;
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::CloneFileAndAppend;
|
||||
using mozilla::dom::quota::IsDatabaseCorruptionError;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::PersistenceType;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -61,7 +56,7 @@ DBAction::~DBAction() = default;
|
||||
void DBAction::RunOnTarget(
|
||||
SafeRefPtr<Resolver> aResolver,
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
Data* aOptionalData) {
|
||||
Data* aOptionalData, const Maybe<CipherKey>& aMaybeCipherKey) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_DIAGNOSTIC_ASSERT(aResolver);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aDirectoryMetadata);
|
||||
@ -89,8 +84,9 @@ void DBAction::RunOnTarget(
|
||||
|
||||
// If there is no previous Action, then we must open one.
|
||||
if (!conn) {
|
||||
QM_TRY_UNWRAP(conn, OpenConnection(*aDirectoryMetadata, *dbDir), QM_VOID,
|
||||
resolveErr);
|
||||
QM_TRY_UNWRAP(conn,
|
||||
OpenConnection(*aDirectoryMetadata, *dbDir, aMaybeCipherKey),
|
||||
QM_VOID, resolveErr);
|
||||
MOZ_DIAGNOSTIC_ASSERT(conn);
|
||||
|
||||
// Save this connection in the shared Data object so later Actions can
|
||||
@ -109,7 +105,8 @@ void DBAction::RunOnTarget(
|
||||
}
|
||||
|
||||
Result<nsCOMPtr<mozIStorageConnection>, nsresult> DBAction::OpenConnection(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBDir) {
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBDir,
|
||||
const Maybe<CipherKey>& aMaybeCipherKey) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_DIAGNOSTIC_ASSERT(aDirectoryMetadata.mDirectoryLockId >= 0);
|
||||
|
||||
@ -124,7 +121,7 @@ Result<nsCOMPtr<mozIStorageConnection>, nsresult> DBAction::OpenConnection(
|
||||
QM_TRY_INSPECT(const auto& dbFile,
|
||||
CloneFileAndAppend(aDBDir, kCachesSQLiteFilename));
|
||||
|
||||
QM_TRY_RETURN(OpenDBConnection(aDirectoryMetadata, *dbFile));
|
||||
QM_TRY_RETURN(OpenDBConnection(aDirectoryMetadata, *dbFile, aMaybeCipherKey));
|
||||
}
|
||||
|
||||
SyncDBAction::SyncDBAction(Mode aMode) : DBAction(aMode) {}
|
||||
@ -145,9 +142,11 @@ void SyncDBAction::RunWithDBOnTarget(
|
||||
}
|
||||
|
||||
Result<nsCOMPtr<mozIStorageConnection>, nsresult> OpenDBConnection(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBFile) {
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBFile,
|
||||
const Maybe<CipherKey>& aMaybeCipherKey) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_DIAGNOSTIC_ASSERT(aDirectoryMetadata.mDirectoryLockId >= -1);
|
||||
MOZ_DIAGNOSTIC_ASSERT_IF(aDirectoryMetadata.mIsPrivate, aMaybeCipherKey);
|
||||
|
||||
// Use our default file:// protocol handler directly to construct the database
|
||||
// URL. This avoids any problems if a plugin registers a custom file://
|
||||
@ -166,10 +165,22 @@ Result<nsCOMPtr<mozIStorageConnection>, nsresult> OpenDBConnection(
|
||||
IntToCString(aDirectoryMetadata.mDirectoryLockId)
|
||||
: EmptyCString();
|
||||
|
||||
const auto keyClause = [&aMaybeCipherKey] {
|
||||
nsAutoCString keyClause;
|
||||
if (aMaybeCipherKey) {
|
||||
keyClause.AssignLiteral("&key=");
|
||||
for (uint8_t byte : CipherStrategy::SerializeKey(*aMaybeCipherKey)) {
|
||||
keyClause.AppendPrintf("%02x", byte);
|
||||
}
|
||||
}
|
||||
return keyClause;
|
||||
}();
|
||||
|
||||
nsCOMPtr<nsIFileURL> dbFileUrl;
|
||||
QM_TRY(MOZ_TO_RESULT(NS_MutateURI(mutator)
|
||||
.SetQuery("cache=private"_ns + directoryLockIdClause)
|
||||
.Finalize(dbFileUrl)));
|
||||
QM_TRY(MOZ_TO_RESULT(
|
||||
NS_MutateURI(mutator)
|
||||
.SetQuery("cache=private"_ns + directoryLockIdClause + keyClause)
|
||||
.Finalize(dbFileUrl)));
|
||||
|
||||
QM_TRY_INSPECT(const auto& storageService,
|
||||
MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
|
||||
|
10
dom/cache/DBAction.h
vendored
10
dom/cache/DBAction.h
vendored
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_cache_DBAction_h
|
||||
#define mozilla_dom_cache_DBAction_h
|
||||
|
||||
#include "CacheCipherKeyManager.h"
|
||||
#include "mozilla/dom/cache/Action.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsString.h"
|
||||
@ -17,7 +18,8 @@ class nsIFile;
|
||||
namespace mozilla::dom::cache {
|
||||
|
||||
Result<nsCOMPtr<mozIStorageConnection>, nsresult> OpenDBConnection(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBFile);
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBFile,
|
||||
const Maybe<CipherKey>& aMaybeCipherKey);
|
||||
|
||||
class DBAction : public Action {
|
||||
protected:
|
||||
@ -41,10 +43,12 @@ class DBAction : public Action {
|
||||
private:
|
||||
void RunOnTarget(SafeRefPtr<Resolver> aResolver,
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
Data* aOptionalData) override;
|
||||
Data* aOptionalData,
|
||||
const Maybe<CipherKey>& aMaybeCipherKey) override;
|
||||
|
||||
Result<nsCOMPtr<mozIStorageConnection>, nsresult> OpenConnection(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBDir);
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aDBDir,
|
||||
const Maybe<CipherKey>& aMaybeCipherKey);
|
||||
|
||||
const Mode mMode;
|
||||
};
|
||||
|
67
dom/cache/FileUtils.cpp
vendored
67
dom/cache/FileUtils.cpp
vendored
@ -6,9 +6,15 @@
|
||||
|
||||
#include "FileUtilsImpl.h"
|
||||
|
||||
#include "CacheCipherKeyManager.h"
|
||||
#include "DBSchema.h"
|
||||
#include "mozilla/dom/InternalResponse.h"
|
||||
#include "mozilla/dom/quota/DecryptingInputStream.h"
|
||||
#include "mozilla/dom/quota/DecryptingInputStream_impl.h"
|
||||
#include "mozilla/dom/quota/EncryptingOutputStream.h"
|
||||
#include "mozilla/dom/quota/EncryptingOutputStream_impl.h"
|
||||
#include "mozilla/dom/quota/FileStreams.h"
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/QuotaObject.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
@ -32,11 +38,8 @@ static_assert(SNAPPY_VERSION == 0x010109);
|
||||
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::CloneFileAndAppend;
|
||||
using mozilla::dom::quota::FileInputStream;
|
||||
using mozilla::dom::quota::FileOutputStream;
|
||||
using mozilla::dom::quota::GetDirEntryKind;
|
||||
using mozilla::dom::quota::nsIFileKind;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
using mozilla::dom::quota::QuotaObject;
|
||||
|
||||
@ -46,6 +49,12 @@ namespace {
|
||||
// XXX This will be tweaked to something more meaningful in Bug 1383656.
|
||||
const int64_t kRoundUpNumber = 20480;
|
||||
|
||||
// At the moment, the encrypted stream block size is assumed to be unchangeable
|
||||
// between encrypting and decrypting blobs. This assumptions holds as long as we
|
||||
// only encrypt in private browsing mode, but when we support encryption for
|
||||
// persistent storage, this needs to be changed.
|
||||
constexpr uint32_t kEncryptedStreamBlockSize = 4096;
|
||||
|
||||
enum BodyFileType { BODY_FILE_FINAL, BODY_FILE_TMP };
|
||||
|
||||
Result<NotNull<nsCOMPtr<nsIFile>>, nsresult> BodyIdToFile(nsIFile& aBaseDir,
|
||||
@ -128,22 +137,15 @@ nsresult BodyDeleteDir(const CacheDirectoryMetadata& aDirectoryMetadata,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Result<std::pair<nsID, nsCOMPtr<nsISupports>>, nsresult> BodyStartWriteStream(
|
||||
Result<nsCOMPtr<nsISupports>, nsresult> BodyStartWriteStream(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
|
||||
const nsID& aBodyId, Maybe<CipherKey> aMaybeCipherKey,
|
||||
nsIInputStream& aSource, void* aClosure, nsAsyncCopyCallbackFun aCallback) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aClosure);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aCallback);
|
||||
|
||||
QM_TRY_INSPECT(const auto& idGen,
|
||||
MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIUUIDGenerator>,
|
||||
MOZ_SELECT_OVERLOAD(do_GetService),
|
||||
"@mozilla.org/uuid-generator;1"));
|
||||
|
||||
nsID id;
|
||||
QM_TRY(MOZ_TO_RESULT(idGen->GenerateUUIDInPlace(&id)));
|
||||
|
||||
QM_TRY_INSPECT(const auto& finalFile,
|
||||
BodyIdToFile(aBaseDir, id, BODY_FILE_FINAL));
|
||||
BodyIdToFile(aBaseDir, aBodyId, BODY_FILE_FINAL));
|
||||
|
||||
{
|
||||
QM_TRY_INSPECT(const bool& exists,
|
||||
@ -153,12 +155,20 @@ Result<std::pair<nsID, nsCOMPtr<nsISupports>>, nsresult> BodyStartWriteStream(
|
||||
}
|
||||
|
||||
QM_TRY_INSPECT(const auto& tmpFile,
|
||||
BodyIdToFile(aBaseDir, id, BODY_FILE_TMP));
|
||||
BodyIdToFile(aBaseDir, aBodyId, BODY_FILE_TMP));
|
||||
|
||||
QM_TRY_UNWRAP(
|
||||
nsCOMPtr<nsIOutputStream> fileStream,
|
||||
CreateFileOutputStream(PERSISTENCE_TYPE_DEFAULT, aDirectoryMetadata,
|
||||
Client::DOMCACHE, tmpFile.get()));
|
||||
QM_TRY_UNWRAP(nsCOMPtr<nsIOutputStream> fileStream,
|
||||
CreateFileOutputStream(aDirectoryMetadata.mPersistenceType,
|
||||
aDirectoryMetadata, Client::DOMCACHE,
|
||||
tmpFile.get()));
|
||||
|
||||
const auto privateBody = aDirectoryMetadata.mIsPrivate;
|
||||
if (privateBody) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aMaybeCipherKey);
|
||||
|
||||
fileStream = MakeRefPtr<quota::EncryptingOutputStream<CipherStrategy>>(
|
||||
std::move(fileStream), kEncryptedStreamBlockSize, *aMaybeCipherKey);
|
||||
}
|
||||
|
||||
const auto compressed = MakeRefPtr<SnappyCompressOutputStream>(fileStream);
|
||||
|
||||
@ -172,7 +182,7 @@ Result<std::pair<nsID, nsCOMPtr<nsISupports>>, nsresult> BodyStartWriteStream(
|
||||
true, // close streams
|
||||
getter_AddRefs(copyContext))));
|
||||
|
||||
return std::make_pair(id, std::move(copyContext));
|
||||
return std::move(copyContext);
|
||||
}
|
||||
|
||||
void BodyCancelWrite(nsISupports& aCopyContext) {
|
||||
@ -203,13 +213,24 @@ nsresult BodyFinalizeWrite(nsIFile& aBaseDir, const nsID& aId) {
|
||||
|
||||
Result<MovingNotNull<nsCOMPtr<nsIInputStream>>, nsresult> BodyOpen(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
|
||||
const nsID& aId) {
|
||||
const nsID& aId, Maybe<CipherKey> aMaybeCipherKey) {
|
||||
QM_TRY_INSPECT(const auto& finalFile,
|
||||
BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL));
|
||||
|
||||
QM_TRY_RETURN(CreateFileInputStream(PERSISTENCE_TYPE_DEFAULT,
|
||||
QM_TRY_UNWRAP(nsCOMPtr<nsIInputStream> fileInputStream,
|
||||
CreateFileInputStream(aDirectoryMetadata.mPersistenceType,
|
||||
aDirectoryMetadata, Client::DOMCACHE,
|
||||
finalFile.get()));
|
||||
|
||||
auto privateBody = aDirectoryMetadata.mIsPrivate;
|
||||
if (privateBody) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aMaybeCipherKey);
|
||||
|
||||
fileInputStream = new quota::DecryptingInputStream<CipherStrategy>(
|
||||
WrapNotNull(std::move(fileInputStream)), kEncryptedStreamBlockSize,
|
||||
*aMaybeCipherKey);
|
||||
}
|
||||
return WrapMovingNotNull(std::move(fileInputStream));
|
||||
}
|
||||
|
||||
nsresult BodyMaybeUpdatePaddingSize(
|
||||
@ -225,7 +246,7 @@ nsresult BodyMaybeUpdatePaddingSize(
|
||||
|
||||
int64_t fileSize = 0;
|
||||
RefPtr<QuotaObject> quotaObject = quotaManager->GetQuotaObject(
|
||||
PERSISTENCE_TYPE_DEFAULT, aDirectoryMetadata, Client::DOMCACHE,
|
||||
aDirectoryMetadata.mPersistenceType, aDirectoryMetadata, Client::DOMCACHE,
|
||||
bodyFile.get(), -1, &fileSize);
|
||||
MOZ_DIAGNOSTIC_ASSERT(quotaObject);
|
||||
MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
|
||||
@ -256,7 +277,7 @@ nsresult BodyDeleteFiles(const CacheDirectoryMetadata& aDirectoryMetadata,
|
||||
[&aDirectoryMetadata, &id](
|
||||
nsIFile& bodyFile,
|
||||
const nsACString& leafName) -> Result<bool, nsresult> {
|
||||
nsID fileId;
|
||||
nsID fileId{};
|
||||
QM_TRY(OkIf(fileId.Parse(leafName.BeginReading())), true,
|
||||
([&aDirectoryMetadata, &bodyFile](const auto) {
|
||||
DebugOnly<nsresult> result = RemoveNsIFile(
|
||||
|
8
dom/cache/FileUtils.h
vendored
8
dom/cache/FileUtils.h
vendored
@ -7,9 +7,10 @@
|
||||
#ifndef mozilla_dom_cache_FileUtils_h
|
||||
#define mozilla_dom_cache_FileUtils_h
|
||||
|
||||
#include "CacheCommon.h"
|
||||
#include "CacheCipherKeyManager.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/cache/Types.h"
|
||||
#include "CacheCommon.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
@ -34,8 +35,9 @@ nsresult BodyDeleteDir(const CacheDirectoryMetadata& aDirectoryMetadata,
|
||||
|
||||
// Returns a Result with a success value with the body id and, optionally, the
|
||||
// copy context.
|
||||
Result<std::pair<nsID, nsCOMPtr<nsISupports>>, nsresult> BodyStartWriteStream(
|
||||
Result<nsCOMPtr<nsISupports>, nsresult> BodyStartWriteStream(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
|
||||
const nsID& aBodyId, Maybe<CipherKey> aMaybeCipherKey,
|
||||
nsIInputStream& aSource, void* aClosure, nsAsyncCopyCallbackFun aCallback);
|
||||
|
||||
void BodyCancelWrite(nsISupports& aCopyContext);
|
||||
@ -44,7 +46,7 @@ nsresult BodyFinalizeWrite(nsIFile& aBaseDir, const nsID& aId);
|
||||
|
||||
Result<MovingNotNull<nsCOMPtr<nsIInputStream>>, nsresult> BodyOpen(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
|
||||
const nsID& aId);
|
||||
const nsID& aId, Maybe<CipherKey> aMaybeCipherKey);
|
||||
|
||||
nsresult BodyMaybeUpdatePaddingSize(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile& aBaseDir,
|
||||
|
89
dom/cache/Manager.cpp
vendored
89
dom/cache/Manager.cpp
vendored
@ -7,6 +7,7 @@
|
||||
#include "mozilla/dom/cache/Manager.h"
|
||||
|
||||
#include "mozilla/AppShutdown.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
@ -30,13 +31,14 @@
|
||||
#include "nsID.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "QuotaClientImpl.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace mozilla::dom::cache {
|
||||
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::CloneFileAndAppend;
|
||||
using mozilla::dom::quota::DirectoryLock;
|
||||
|
||||
@ -67,6 +69,24 @@ nsresult MaybeUpdatePaddingFile(nsIFile* aBaseDir, mozIStorageConnection* aConn,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Maybe<CipherKey> GetOrCreateCipherKey(NotNull<Context*> aContext,
|
||||
const nsID& aBodyId, bool aCreate) {
|
||||
const auto& maybeMetadata = aContext->MaybeCacheDirectoryMetadataRef();
|
||||
MOZ_DIAGNOSTIC_ASSERT(maybeMetadata);
|
||||
|
||||
auto privateOrigin = maybeMetadata->mIsPrivate;
|
||||
if (!privateOrigin) {
|
||||
return Nothing{};
|
||||
}
|
||||
|
||||
nsCString bodyIdStr{aBodyId.ToString().get()};
|
||||
|
||||
auto& cipherKeyManager = aContext->MutableCipherKeyManagerRef();
|
||||
|
||||
return aCreate ? Some(cipherKeyManager.Ensure(bodyIdStr))
|
||||
: cipherKeyManager.Get(bodyIdStr);
|
||||
}
|
||||
|
||||
// An Action that is executed when a Context is first created. It ensures that
|
||||
// the directory and database are setup properly. This lets other actions
|
||||
// not worry about these details.
|
||||
@ -173,7 +193,8 @@ class DeleteOrphanedBodyAction final : public Action {
|
||||
|
||||
void RunOnTarget(SafeRefPtr<Resolver> aResolver,
|
||||
const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
|
||||
Data*) override {
|
||||
Data*,
|
||||
const Maybe<CipherKey>& /*aMaybeCipherKey*/) override {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aResolver);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aDirectoryMetadata);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aDirectoryMetadata->mDir);
|
||||
@ -635,10 +656,15 @@ class Manager::CacheMatchAction final : public Manager::BaseAction {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const auto& bodyId = mResponse.mBodyId;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
if (mArgs.openMode() == OpenMode::Eager) {
|
||||
QM_TRY_UNWRAP(stream,
|
||||
BodyOpen(aDirectoryMetadata, *aDBDir, mResponse.mBodyId));
|
||||
QM_TRY_UNWRAP(
|
||||
stream,
|
||||
BodyOpen(aDirectoryMetadata, *aDBDir, bodyId,
|
||||
GetOrCreateCipherKey(WrapNotNull(mManager->mContext), bodyId,
|
||||
/* aCreate */ false)));
|
||||
}
|
||||
|
||||
mStreamList->Add(mResponse.mBodyId, std::move(stream));
|
||||
@ -697,10 +723,15 @@ class Manager::CacheMatchAllAction final : public Manager::BaseAction {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& bodyId = mSavedResponses[i].mBodyId;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
if (mArgs.openMode() == OpenMode::Eager) {
|
||||
QM_TRY_UNWRAP(stream, BodyOpen(aDirectoryMetadata, *aDBDir,
|
||||
mSavedResponses[i].mBodyId));
|
||||
QM_TRY_UNWRAP(stream,
|
||||
BodyOpen(aDirectoryMetadata, *aDBDir, bodyId,
|
||||
GetOrCreateCipherKey(
|
||||
WrapNotNull(mManager->mContext), bodyId,
|
||||
/* aCreate */ false)));
|
||||
}
|
||||
|
||||
mStreamList->Add(mSavedResponses[i].mBodyId, std::move(stream));
|
||||
@ -971,12 +1002,12 @@ class Manager::CachePutAllAction final : public DBAction {
|
||||
struct Entry {
|
||||
CacheRequest mRequest;
|
||||
nsCOMPtr<nsIInputStream> mRequestStream;
|
||||
nsID mRequestBodyId;
|
||||
nsID mRequestBodyId{};
|
||||
nsCOMPtr<nsISupports> mRequestCopyContext;
|
||||
|
||||
CacheResponse mResponse;
|
||||
nsCOMPtr<nsIInputStream> mResponseStream;
|
||||
nsID mResponseBodyId;
|
||||
nsID mResponseBodyId{};
|
||||
nsCOMPtr<nsISupports> mResponseCopyContext;
|
||||
};
|
||||
|
||||
@ -1001,10 +1032,22 @@ class Manager::CachePutAllAction final : public DBAction {
|
||||
if (!source) {
|
||||
return NS_OK;
|
||||
}
|
||||
QM_TRY_INSPECT(const auto& idGen,
|
||||
MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIUUIDGenerator>,
|
||||
MOZ_SELECT_OVERLOAD(do_GetService),
|
||||
"@mozilla.org/uuid-generator;1"));
|
||||
|
||||
QM_TRY_INSPECT((const auto& [bodyId, copyContext]),
|
||||
BodyStartWriteStream(aDirectoryMetadata, *mDBDir, *source,
|
||||
this, AsyncCopyCompleteFunc));
|
||||
nsID bodyId{};
|
||||
QM_TRY(MOZ_TO_RESULT(idGen->GenerateUUIDInPlace(&bodyId)));
|
||||
|
||||
Maybe<CipherKey> maybeKey =
|
||||
GetOrCreateCipherKey(WrapNotNull(mManager->mContext), bodyId,
|
||||
/* aCreate */ true);
|
||||
|
||||
QM_TRY_INSPECT(
|
||||
const auto& copyContext,
|
||||
BodyStartWriteStream(aDirectoryMetadata, *mDBDir, bodyId, maybeKey,
|
||||
*source, this, AsyncCopyCompleteFunc));
|
||||
|
||||
if (aStreamId == RequestStream) {
|
||||
aEntry.mRequestBodyId = bodyId;
|
||||
@ -1218,10 +1261,15 @@ class Manager::CacheKeysAction final : public Manager::BaseAction {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& bodyId = mSavedRequests[i].mBodyId;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
if (mArgs.openMode() == OpenMode::Eager) {
|
||||
QM_TRY_UNWRAP(stream, BodyOpen(aDirectoryMetadata, *aDBDir,
|
||||
mSavedRequests[i].mBodyId));
|
||||
QM_TRY_UNWRAP(stream,
|
||||
BodyOpen(aDirectoryMetadata, *aDBDir, bodyId,
|
||||
GetOrCreateCipherKey(
|
||||
WrapNotNull(mManager->mContext), bodyId,
|
||||
/* aCreate */ false)));
|
||||
}
|
||||
|
||||
mStreamList->Add(mSavedRequests[i].mBodyId, std::move(stream));
|
||||
@ -1283,10 +1331,15 @@ class Manager::StorageMatchAction final : public Manager::BaseAction {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const auto& bodyId = mSavedResponse.mBodyId;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
if (mArgs.openMode() == OpenMode::Eager) {
|
||||
QM_TRY_UNWRAP(stream, BodyOpen(aDirectoryMetadata, *aDBDir,
|
||||
mSavedResponse.mBodyId));
|
||||
QM_TRY_UNWRAP(
|
||||
stream,
|
||||
BodyOpen(aDirectoryMetadata, *aDBDir, bodyId,
|
||||
GetOrCreateCipherKey(WrapNotNull(mManager->mContext), bodyId,
|
||||
/* aCreate */ false)));
|
||||
}
|
||||
|
||||
mStreamList->Add(mSavedResponse.mBodyId, std::move(stream));
|
||||
@ -1511,7 +1564,11 @@ class Manager::OpenStreamAction final : public Manager::BaseAction {
|
||||
mozIStorageConnection* aConn) override {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aDBDir);
|
||||
|
||||
QM_TRY_UNWRAP(mBodyStream, BodyOpen(aDirectoryMetadata, *aDBDir, mBodyId));
|
||||
QM_TRY_UNWRAP(
|
||||
mBodyStream,
|
||||
BodyOpen(aDirectoryMetadata, *aDBDir, mBodyId,
|
||||
GetOrCreateCipherKey(WrapNotNull(mManager->mContext), mBodyId,
|
||||
/* aCreate */ false)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
52
dom/cache/QuotaClient.cpp
vendored
52
dom/cache/QuotaClient.cpp
vendored
@ -8,11 +8,13 @@
|
||||
|
||||
#include "DBAction.h"
|
||||
#include "FileUtilsImpl.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/cache/DBSchema.h"
|
||||
#include "mozilla/dom/cache/Manager.h"
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "mozilla/dom/quota/QuotaCommon.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/UsageInfo.h"
|
||||
@ -29,8 +31,7 @@ using mozilla::dom::quota::DatabaseUsageType;
|
||||
using mozilla::dom::quota::GetDirEntryKind;
|
||||
using mozilla::dom::quota::nsIFileKind;
|
||||
using mozilla::dom::quota::OriginMetadata;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::PersistenceType;
|
||||
using mozilla::dom::quota::PrincipalMetadata;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
using mozilla::dom::quota::UsageInfo;
|
||||
using mozilla::ipc::AssertIsOnBackgroundThread;
|
||||
@ -123,7 +124,8 @@ Result<UsageInfo, nsresult> GetBodyUsage(nsIFile& aMorgueDir,
|
||||
}
|
||||
|
||||
Result<int64_t, nsresult> GetPaddingSizeFromDB(
|
||||
nsIFile& aDir, nsIFile& aDBFile, const OriginMetadata& aOriginMetadata) {
|
||||
nsIFile& aDir, nsIFile& aDBFile, const OriginMetadata& aOriginMetadata,
|
||||
const Maybe<CipherKey>& aMaybeCipherKey) {
|
||||
CacheDirectoryMetadata directoryMetadata(aOriginMetadata);
|
||||
// directoryMetadata.mDirectoryLockId must be -1 (which is default for new
|
||||
// CacheDirectoryMetadata) because this method should only be called from
|
||||
@ -142,7 +144,7 @@ Result<int64_t, nsresult> GetPaddingSizeFromDB(
|
||||
#endif
|
||||
|
||||
QM_TRY_INSPECT(const auto& conn,
|
||||
OpenDBConnection(directoryMetadata, aDBFile));
|
||||
OpenDBConnection(directoryMetadata, aDBFile, aMaybeCipherKey));
|
||||
|
||||
// Make sure that the database has the latest schema before we try to read
|
||||
// from it. We have to do this because GetPaddingSizeFromDB is called
|
||||
@ -236,10 +238,19 @@ Result<UsageInfo, nsresult> CacheQuotaClient::InitOrigin(
|
||||
// XXX Ensure the -wel file is removed if the caches.sqlite doesn't exist.
|
||||
QM_TRY(OkIf(!!cachesSQLiteFile), UsageInfo{});
|
||||
|
||||
const auto maybeCipherKey = [this, &aOriginMetadata] {
|
||||
Maybe<CipherKey> maybeCipherKey;
|
||||
auto cipherKeyManager = GetOrCreateCipherKeyManager(aOriginMetadata);
|
||||
if (cipherKeyManager) {
|
||||
maybeCipherKey = Some(cipherKeyManager->Ensure());
|
||||
}
|
||||
return maybeCipherKey;
|
||||
}();
|
||||
|
||||
QM_TRY_INSPECT(
|
||||
const auto& paddingSize,
|
||||
([dir, cachesSQLiteFile,
|
||||
&aOriginMetadata]() -> Result<int64_t, nsresult> {
|
||||
([dir, cachesSQLiteFile, &aOriginMetadata,
|
||||
&maybeCipherKey]() -> Result<int64_t, nsresult> {
|
||||
if (!DirectoryPaddingFileExists(*dir, DirPaddingFile::TMP_FILE)) {
|
||||
QM_WARNONLY_TRY_UNWRAP(const auto maybePaddingSize,
|
||||
DirectoryPaddingGet(*dir));
|
||||
@ -251,8 +262,8 @@ Result<UsageInfo, nsresult> CacheQuotaClient::InitOrigin(
|
||||
// If the temporary file still exists or failing to get the padding size
|
||||
// from the padding file, then we need to get the padding size from the
|
||||
// database and restore the padding file.
|
||||
QM_TRY_RETURN(
|
||||
GetPaddingSizeFromDB(*dir, *cachesSQLiteFile, aOriginMetadata));
|
||||
QM_TRY_RETURN(GetPaddingSizeFromDB(*dir, *cachesSQLiteFile,
|
||||
aOriginMetadata, maybeCipherKey));
|
||||
}()));
|
||||
|
||||
QM_TRY_INSPECT(
|
||||
@ -343,13 +354,20 @@ Result<UsageInfo, nsresult> CacheQuotaClient::GetUsageForOrigin(
|
||||
QuotaManager* quotaManager = QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
return quotaManager->GetUsageForClient(PERSISTENCE_TYPE_DEFAULT,
|
||||
return quotaManager->GetUsageForClient(aOriginMetadata.mPersistenceType,
|
||||
aOriginMetadata, Client::DOMCACHE);
|
||||
}
|
||||
|
||||
void CacheQuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
|
||||
const nsACString& aOrigin) {
|
||||
// Nothing to do here.
|
||||
AssertIsOnIOThread();
|
||||
|
||||
if (aPersistenceType == quota::PERSISTENCE_TYPE_PRIVATE) {
|
||||
if (auto entry = mCipherKeyManagers.Lookup(aOrigin)) {
|
||||
entry.Data()->Invalidate();
|
||||
entry.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CacheQuotaClient::OnRepositoryClearCompleted(
|
||||
@ -492,6 +510,20 @@ nsresult CacheQuotaClient::WipePaddingFileInternal(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<CipherKeyManager> CacheQuotaClient::GetOrCreateCipherKeyManager(
|
||||
const PrincipalMetadata& aMetadata) {
|
||||
AssertIsOnIOThread();
|
||||
|
||||
auto privateOrigin = aMetadata.mIsPrivate;
|
||||
if (!privateOrigin) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& origin = aMetadata.mOrigin;
|
||||
return mCipherKeyManagers.LookupOrInsertWith(
|
||||
origin, [] { return new CipherKeyManager("CacheCipherKeyManager"); });
|
||||
}
|
||||
|
||||
CacheQuotaClient::~CacheQuotaClient() {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(sInstance == this);
|
||||
|
9
dom/cache/QuotaClientImpl.h
vendored
9
dom/cache/QuotaClientImpl.h
vendored
@ -7,9 +7,12 @@
|
||||
#ifndef mozilla_dom_cache_QuotaClientImpl_h
|
||||
#define mozilla_dom_cache_QuotaClientImpl_h
|
||||
|
||||
#include "CacheCipherKeyManager.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/QMResult.h"
|
||||
#include "mozilla/dom/cache/QuotaClient.h"
|
||||
#include "mozilla/dom/cache/FileUtils.h"
|
||||
#include "mozilla/dom/cache/Types.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
|
||||
namespace mozilla::dom::cache {
|
||||
@ -119,6 +122,9 @@ class CacheQuotaClient final : public quota::Client {
|
||||
nsresult WipePaddingFileInternal(
|
||||
const CacheDirectoryMetadata& aDirectoryMetadata, nsIFile* aBaseDir);
|
||||
|
||||
RefPtr<CipherKeyManager> GetOrCreateCipherKeyManager(
|
||||
const quota::PrincipalMetadata& aMetadata);
|
||||
|
||||
private:
|
||||
~CacheQuotaClient();
|
||||
|
||||
@ -128,6 +134,9 @@ class CacheQuotaClient final : public quota::Client {
|
||||
void ForceKillActors() override;
|
||||
void FinalizeShutdown() override;
|
||||
|
||||
// Should always be accessed from QM IO thread.
|
||||
nsTHashMap<nsCStringHashKey, RefPtr<CipherKeyManager>> mCipherKeyManagers;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheQuotaClient, override)
|
||||
};
|
||||
|
||||
|
14
dom/cache/Types.h
vendored
14
dom/cache/Types.h
vendored
@ -10,6 +10,7 @@
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
#include "mozilla/dom/quota/CommonMetadata.h"
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIInputStream.h"
|
||||
@ -31,17 +32,20 @@ struct CacheDirectoryMetadata : quota::ClientMetadata {
|
||||
nsCOMPtr<nsIFile> mDir;
|
||||
int64_t mDirectoryLockId = -1;
|
||||
|
||||
explicit CacheDirectoryMetadata(quota::PrincipalMetadata aPrincipalMetadata)
|
||||
explicit CacheDirectoryMetadata(
|
||||
const quota::PrincipalMetadata& aPrincipalMetadata)
|
||||
: quota::ClientMetadata(
|
||||
quota::OriginMetadata{std::move(aPrincipalMetadata),
|
||||
quota::PERSISTENCE_TYPE_DEFAULT},
|
||||
quota::OriginMetadata{aPrincipalMetadata,
|
||||
aPrincipalMetadata.mIsPrivate
|
||||
? quota::PERSISTENCE_TYPE_PRIVATE
|
||||
: quota::PERSISTENCE_TYPE_DEFAULT},
|
||||
quota::Client::Type::DOMCACHE) {}
|
||||
|
||||
explicit CacheDirectoryMetadata(quota::OriginMetadata aOriginMetadata)
|
||||
: quota::ClientMetadata(std::move(aOriginMetadata),
|
||||
quota::Client::Type::DOMCACHE) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aOriginMetadata.mPersistenceType ==
|
||||
quota::PERSISTENCE_TYPE_DEFAULT);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mPersistenceType == quota::PERSISTENCE_TYPE_DEFAULT ||
|
||||
mPersistenceType == quota::PERSISTENCE_TYPE_PRIVATE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11605,6 +11605,10 @@ DatabaseFileManager::DatabaseFileManager(
|
||||
mOriginMetadata(aOriginMetadata),
|
||||
mDatabaseName(aDatabaseName),
|
||||
mDatabaseID(aDatabaseID),
|
||||
mCipherKeyManager(
|
||||
aIsInPrivateBrowsingMode
|
||||
? new IndexedDBCipherKeyManager("IndexedDBCipherKeyManager")
|
||||
: nullptr),
|
||||
mEnforcingQuota(aEnforcingQuota),
|
||||
mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {}
|
||||
|
||||
@ -11963,7 +11967,9 @@ nsresult DatabaseFileManager::SyncDeleteFile(nsIFile& aFile,
|
||||
}
|
||||
|
||||
nsresult DatabaseFileManager::Invalidate() {
|
||||
mCipherKeyManager.Invalidate();
|
||||
if (mCipherKeyManager) {
|
||||
mCipherKeyManager->Invalidate();
|
||||
}
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(FileInfoManager::Invalidate()));
|
||||
|
||||
|
@ -32,7 +32,7 @@ class DatabaseFileManager final
|
||||
const nsString mDatabaseName;
|
||||
const nsCString mDatabaseID;
|
||||
|
||||
mutable IndexedDBCipherKeyManager mCipherKeyManager;
|
||||
RefPtr<IndexedDBCipherKeyManager> mCipherKeyManager;
|
||||
|
||||
LazyInitializedOnce<const nsString> mDirectoryPath;
|
||||
LazyInitializedOnce<const nsString> mJournalDirectoryPath;
|
||||
@ -84,7 +84,10 @@ class DatabaseFileManager final
|
||||
const nsCString& DatabaseID() const { return mDatabaseID; }
|
||||
|
||||
IndexedDBCipherKeyManager& MutableCipherKeyManagerRef() const {
|
||||
return mCipherKeyManager;
|
||||
MOZ_ASSERT(mIsInPrivateBrowsingMode);
|
||||
MOZ_ASSERT(mCipherKeyManager);
|
||||
|
||||
return *mCipherKeyManager;
|
||||
}
|
||||
|
||||
auto IsInPrivateBrowsingMode() const { return mIsInPrivateBrowsingMode; }
|
||||
|
@ -1,53 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; 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 "IndexedDBCipherKeyManager.h"
|
||||
|
||||
#include "mozilla/dom/quota/QuotaCommon.h"
|
||||
|
||||
namespace mozilla::dom::indexedDB {
|
||||
|
||||
Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsACString& aKeyId) {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
|
||||
return lockedCipherKeys->MaybeGet(aKeyId);
|
||||
}
|
||||
|
||||
CipherKey IndexedDBCipherKeyManager::Ensure(const nsACString& aKeyId) {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
|
||||
return lockedCipherKeys->LookupOrInsertWith(aKeyId, [] {
|
||||
// Generate a new key if one corresponding to keyStoreId does not exist
|
||||
// already.
|
||||
|
||||
QM_TRY_RETURN(IndexedDBCipherStrategy::GenerateKey(), [](const auto&) {
|
||||
// Bug1800110 Propagate the error to the caller rather than asserting.
|
||||
MOZ_RELEASE_ASSERT(false);
|
||||
|
||||
return CipherKey{};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool IndexedDBCipherKeyManager::Invalidated() {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
return mInvalidated;
|
||||
}
|
||||
|
||||
void IndexedDBCipherKeyManager::Invalidate() {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
mInvalidated.Flip();
|
||||
|
||||
lockedCipherKeys->Clear();
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom::indexedDB
|
@ -7,47 +7,25 @@
|
||||
#ifndef DOM_INDEXEDDB_INDEXEDDBCIPHERKEYMANAGER_H_
|
||||
#define DOM_INDEXEDDB_INDEXEDDBCIPHERKEYMANAGER_H_
|
||||
|
||||
#include "FlippedOnce.h"
|
||||
#include "mozilla/DataMutex.h"
|
||||
#include "mozilla/dom/quota/CipherKeyManager.h"
|
||||
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
|
||||
#include "nsTHashMap.h"
|
||||
|
||||
namespace mozilla::dom::indexedDB {
|
||||
namespace mozilla::dom {
|
||||
|
||||
using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy;
|
||||
// IndexedDBCipherKeyManager is used by IndexedDB operations to store/retrieve
|
||||
// keys in private browsing mode. All data in IndexedDB must be encrypted
|
||||
// using a cipher key and unique IV (Initialization Vector). While there's a
|
||||
// separate cipher key for every blob file; the SQLite database gets encrypted
|
||||
// using the commmon database key. All keys pertaining to a single IndexedDB
|
||||
// database get stored together using quota::CipherKeyManager. So, the hashmap
|
||||
// can be used to look up the common database key and blob keys using "default"
|
||||
// and blob file ids respectively.
|
||||
|
||||
using IndexedDBCipherStrategy = mozilla::dom::quota::IPCStreamCipherStrategy;
|
||||
using IndexedDBCipherKeyManager =
|
||||
mozilla::dom::quota::CipherKeyManager<IndexedDBCipherStrategy>;
|
||||
using CipherKey = IndexedDBCipherStrategy::KeyType;
|
||||
|
||||
class IndexedDBCipherKeyManager {
|
||||
// This helper class is used by IndexedDB operations to store/retrieve cipher
|
||||
// keys in private browsing mode. All data in IndexedDB must be encrypted
|
||||
// using a cipher key and unique IV (Initialization Vector). While there's a
|
||||
// separate cipher key for every blob file; the SQLite database gets encrypted
|
||||
// using the commmon database key. All keys pertaining to a single IndexedDB
|
||||
// database get stored together in a hashmap. So the hashmap can be used to
|
||||
// to look up the common database key and blob keys using "default" and blob
|
||||
// file ids respectively.
|
||||
|
||||
public:
|
||||
IndexedDBCipherKeyManager() : mCipherKeys("IndexedDBCipherKeyManager"){};
|
||||
|
||||
Maybe<CipherKey> Get(const nsACString& aKeyId = "default"_ns);
|
||||
|
||||
CipherKey Ensure(const nsACString& aKeyId = "default"_ns);
|
||||
|
||||
bool Invalidated();
|
||||
|
||||
// After calling this method, callers should not call any more methods on this
|
||||
// class.
|
||||
void Invalidate();
|
||||
|
||||
private:
|
||||
// XXX Maybe we can avoid a mutex here by moving all accesses to the
|
||||
// background thread.
|
||||
DataMutex<nsTHashMap<nsCStringHashKey, CipherKey>> mCipherKeys;
|
||||
|
||||
FlippedOnce<false> mInvalidated;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom::indexedDB
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // DOM_INDEXEDDB_INDEXEDDBCIPHERKEYMANAGER_H_
|
||||
|
@ -69,7 +69,6 @@ UNIFIED_SOURCES += [
|
||||
"IDBTransaction.cpp",
|
||||
"IndexedDatabase.cpp",
|
||||
"IndexedDatabaseManager.cpp",
|
||||
"IndexedDBCipherKeyManager.cpp",
|
||||
"IndexedDBCommon.cpp",
|
||||
"KeyPath.cpp",
|
||||
"ProfilerHelpers.cpp",
|
||||
|
97
dom/quota/CipherKeyManager.h
Normal file
97
dom/quota/CipherKeyManager.h
Normal file
@ -0,0 +1,97 @@
|
||||
/* -*- Mode: C++; tab-width: 8; 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/. */
|
||||
|
||||
#ifndef DOM_QUOTA_CIPHERKEYMANAGER_H_
|
||||
#define DOM_QUOTA_CIPHERKEYMANAGER_H_
|
||||
|
||||
#include "mozilla/DataMutex.h"
|
||||
#include "mozilla/dom/FlippedOnce.h"
|
||||
#include "mozilla/dom/quota/QuotaCommon.h"
|
||||
#include "nsTHashMap.h"
|
||||
|
||||
namespace mozilla::dom::quota {
|
||||
|
||||
using mozilla::FlippedOnce;
|
||||
|
||||
template <typename CipherStrategy>
|
||||
class CipherKeyManager {
|
||||
// This helper class is used by quota clients to store/retrieve cipher
|
||||
// keys in private browsing mode. All data in private mode must be encrypted
|
||||
// using a cipher key and unique IV (Initialization Vector).
|
||||
|
||||
// This class uses hashmap (represented by mCipherKeys) to store cipher keys
|
||||
// and is currently used by IndexedDB and Cache quota clients. At any given
|
||||
// time, IndexedDB may contain multiple instances of this class where each is
|
||||
// used to cipherkeys relevant to a particular database. Unlike IndexedDB,
|
||||
// CacheAPI only has one physical sqlite db per origin, so all cipher keys
|
||||
// corresponding to an origin in cacheAPI gets stored together in this
|
||||
// hashmap.
|
||||
|
||||
// Bug1859558: It could be better if QuotaManager owns cipherKeys for
|
||||
// all the quota clients and exposes, methods like
|
||||
// GetOrCreateCipherManager(aOrigin, aDatabaseName, aClientType) for
|
||||
// clients to access their respective cipherKeys scoped.
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CipherKeyManager)
|
||||
|
||||
using CipherKey = typename CipherStrategy::KeyType;
|
||||
|
||||
public:
|
||||
explicit CipherKeyManager(const char* aName) : mCipherKeys(aName){};
|
||||
|
||||
Maybe<CipherKey> Get(const nsACString& aKeyId = "default"_ns) {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
|
||||
return lockedCipherKeys->MaybeGet(aKeyId);
|
||||
}
|
||||
|
||||
CipherKey Ensure(const nsACString& aKeyId = "default"_ns) {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
|
||||
return lockedCipherKeys->LookupOrInsertWith(aKeyId, [] {
|
||||
// Generate a new key if one corresponding to keyStoreId does not exist
|
||||
// already.
|
||||
|
||||
QM_TRY_RETURN(CipherStrategy::GenerateKey(), [](const auto&) {
|
||||
// Bug1800110 Propagate the error to the caller rather than asserting.
|
||||
MOZ_RELEASE_ASSERT(false);
|
||||
|
||||
return CipherKey{};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool Invalidated() {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
return mInvalidated;
|
||||
}
|
||||
|
||||
// After calling this method, callers should not call any more methods on this
|
||||
// class.
|
||||
void Invalidate() {
|
||||
auto lockedCipherKeys = mCipherKeys.Lock();
|
||||
|
||||
mInvalidated.Flip();
|
||||
|
||||
lockedCipherKeys->Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
~CipherKeyManager() = default;
|
||||
// XXX Maybe we can avoid a mutex here by moving all accesses to the
|
||||
// background thread.
|
||||
DataMutex<nsTHashMap<nsCStringHashKey, CipherKey>> mCipherKeys;
|
||||
|
||||
FlippedOnce<false> mInvalidated;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom::quota
|
||||
|
||||
#endif // DOM_QUOTA_CIPHERKEYMANAGER_H_
|
@ -29,6 +29,7 @@ EXPORTS.mozilla.dom.quota += [
|
||||
"AssertionsImpl.h",
|
||||
"CachingDatabaseConnection.h",
|
||||
"CheckedUnsafePtr.h",
|
||||
"CipherKeyManager.h",
|
||||
"CipherStrategy.h",
|
||||
"Client.h",
|
||||
"ClientImpl.h",
|
||||
|
Loading…
Reference in New Issue
Block a user