зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset a266f691a68c (bug 1784966) for causing IDB Encryption related failures CLOSED TREE
This commit is contained in:
Родитель
07d2c243cb
Коммит
964b5b473b
|
@ -32,7 +32,6 @@
|
|||
#include "IndexedDBCommon.h"
|
||||
#include "IndexedDatabaseInlines.h"
|
||||
#include "IndexedDatabaseManager.h"
|
||||
#include "IndexedDBCipherKeyManager.h"
|
||||
#include "KeyPath.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "ProfilerHelpers.h"
|
||||
|
@ -2385,6 +2384,7 @@ class Database final
|
|||
|
||||
bool IsInPrivateBrowsing() const {
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
return mInPrivateBrowsing;
|
||||
}
|
||||
|
||||
|
@ -3750,47 +3750,6 @@ class NormalTransactionOp : public TransactionDatabaseOperationBase,
|
|||
const PreprocessResponse& aResponse) final;
|
||||
};
|
||||
|
||||
Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsCString& aDatabaseID,
|
||||
const nsCString& keyStoreID) {
|
||||
auto lockedPrivateBrowsingInfoHashTable =
|
||||
mPrivateBrowsingInfoHashTable.Lock();
|
||||
|
||||
auto dbKeyStore = lockedPrivateBrowsingInfoHashTable->Lookup(aDatabaseID);
|
||||
if (!dbKeyStore) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
return dbKeyStore->MaybeGet(keyStoreID);
|
||||
}
|
||||
|
||||
CipherKey IndexedDBCipherKeyManager::Ensure(const nsCString& aDatabaseID,
|
||||
const nsCString& keyStoreID) {
|
||||
auto lockedPrivateBrowsingInfoHashTable =
|
||||
mPrivateBrowsingInfoHashTable.Lock();
|
||||
|
||||
auto& dbKeyStore =
|
||||
lockedPrivateBrowsingInfoHashTable->LookupOrInsert(aDatabaseID);
|
||||
return dbKeyStore.LookupOrInsertWith(keyStoreID, [] {
|
||||
// XXX Generate key using proper random data, such that we can ensure
|
||||
// the use of unique IVs per key by discriminating by database's file
|
||||
// id & offset.
|
||||
auto keyOrErr = IndexedDBCipherStrategy::GenerateKey();
|
||||
|
||||
// XXX Propagate the error to the caller rather than asserting.
|
||||
return keyOrErr.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
bool IndexedDBCipherKeyManager::Remove(const nsCString& aDatabaseID) {
|
||||
auto lockedPrivateBrowsingInfoHashTable =
|
||||
mPrivateBrowsingInfoHashTable.Lock();
|
||||
return lockedPrivateBrowsingInfoHashTable->Remove(aDatabaseID);
|
||||
}
|
||||
|
||||
// XXX Maybe we can avoid a mutex here by moving all accesses to the background
|
||||
// thread.
|
||||
StaticAutoPtr<IndexedDBCipherKeyManager> gIndexedDBCipherKeyManager;
|
||||
|
||||
class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
|
||||
friend class TransactionBase;
|
||||
|
||||
|
@ -3806,7 +3765,7 @@ class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
|
|||
#ifdef DEBUG
|
||||
const StructuredCloneFileBase::FileType mType;
|
||||
#endif
|
||||
void EnsureCipherKey();
|
||||
|
||||
void AssertInvariants() const;
|
||||
|
||||
explicit StoredFileInfo(SafeRefPtr<DatabaseFileInfo> aFileInfo);
|
||||
|
@ -3940,18 +3899,6 @@ void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const {
|
|||
}
|
||||
}
|
||||
|
||||
void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() {
|
||||
const auto& fileInfo = GetFileInfo();
|
||||
const auto& fileMgr = fileInfo.Manager();
|
||||
|
||||
// no need to generate cipher keys if we are not in PBM
|
||||
if (!fileMgr.IsInPrivateBrowsingMode()) return;
|
||||
|
||||
nsCString keyId;
|
||||
keyId.AppendInt(fileInfo.Id());
|
||||
gIndexedDBCipherKeyManager->Ensure(fileMgr.DatabaseID(), keyId);
|
||||
}
|
||||
|
||||
ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
|
||||
SafeRefPtr<DatabaseFileInfo> aFileInfo)
|
||||
: mFileInfo{WrapNotNull(std::move(aFileInfo))},
|
||||
|
@ -3964,7 +3911,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
|
|||
AssertIsOnBackgroundThread();
|
||||
AssertInvariants();
|
||||
|
||||
EnsureCipherKey();
|
||||
MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
|
||||
}
|
||||
|
||||
|
@ -3980,7 +3926,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
|
|||
AssertIsOnBackgroundThread();
|
||||
AssertInvariants();
|
||||
|
||||
EnsureCipherKey();
|
||||
MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
|
||||
}
|
||||
|
||||
|
@ -3997,7 +3942,6 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
|
|||
AssertIsOnBackgroundThread();
|
||||
AssertInvariants();
|
||||
|
||||
EnsureCipherKey();
|
||||
MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
|
||||
}
|
||||
|
||||
|
@ -5611,26 +5555,19 @@ class EncryptedFileBlobImpl final : public FileBlobImpl {
|
|||
}
|
||||
|
||||
private:
|
||||
const CipherKey mKey;
|
||||
const CipherKey& mKey;
|
||||
};
|
||||
|
||||
RefPtr<BlobImpl> CreateFileBlobImpl(const Database& aDatabase,
|
||||
const nsCOMPtr<nsIFile>& aNativeFile,
|
||||
const DatabaseFileInfo::IdType aId) {
|
||||
if (aDatabase.IsInPrivateBrowsing()) {
|
||||
nsCString cipherKeyId;
|
||||
cipherKeyId.AppendInt(aId);
|
||||
|
||||
const auto& key =
|
||||
gIndexedDBCipherKeyManager->Get(aDatabase.Id(), cipherKeyId);
|
||||
|
||||
MOZ_RELEASE_ASSERT(key.isSome());
|
||||
return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *key);
|
||||
const auto& maybeKey = aDatabase.MaybeKeyRef();
|
||||
if (maybeKey) {
|
||||
return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *maybeKey);
|
||||
}
|
||||
|
||||
auto impl = MakeRefPtr<FileBlobImpl>(aNativeFile);
|
||||
impl->SetFileId(aId);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
|
@ -6075,6 +6012,12 @@ using DatabaseActorHashtable =
|
|||
|
||||
StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
|
||||
|
||||
using PrivateBrowsingInfoHashtable = nsTHashMap<nsCStringHashKey, CipherKey>;
|
||||
// XXX Maybe we can avoid a mutex here by moving all accesses to the background
|
||||
// thread.
|
||||
StaticAutoPtr<DataMutex<PrivateBrowsingInfoHashtable>>
|
||||
gPrivateBrowsingInfoHashtable;
|
||||
|
||||
StaticRefPtr<ConnectionPool> gConnectionPool;
|
||||
|
||||
StaticRefPtr<FileHandleThreadPool> gFileHandleThreadPool;
|
||||
|
@ -6108,8 +6051,9 @@ void IncreaseBusyCount() {
|
|||
MOZ_ASSERT(!gLiveDatabaseHashtable);
|
||||
gLiveDatabaseHashtable = new DatabaseActorHashtable();
|
||||
|
||||
MOZ_ASSERT(!gIndexedDBCipherKeyManager);
|
||||
gIndexedDBCipherKeyManager = new IndexedDBCipherKeyManager();
|
||||
MOZ_ASSERT(!gPrivateBrowsingInfoHashtable);
|
||||
gPrivateBrowsingInfoHashtable = new DataMutex<PrivateBrowsingInfoHashtable>(
|
||||
"gPrivateBrowsingInfoHashtable");
|
||||
|
||||
MOZ_ASSERT(!gLoggingInfoHashtable);
|
||||
gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
|
||||
|
@ -6157,10 +6101,11 @@ void DecreaseBusyCount() {
|
|||
MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
|
||||
gLiveDatabaseHashtable = nullptr;
|
||||
|
||||
MOZ_ASSERT(gIndexedDBCipherKeyManager);
|
||||
MOZ_ASSERT(gPrivateBrowsingInfoHashtable);
|
||||
// XXX After we add the private browsing session end listener, we can assert
|
||||
// this.
|
||||
gIndexedDBCipherKeyManager = nullptr;
|
||||
// MOZ_ASSERT(!gPrivateBrowsingInfoHashtable->Count());
|
||||
gPrivateBrowsingInfoHashtable = nullptr;
|
||||
|
||||
MOZ_ASSERT(gFactoryOps);
|
||||
MOZ_ASSERT(gFactoryOps->IsEmpty());
|
||||
|
@ -12119,14 +12064,11 @@ DatabaseFileManager::MutexType DatabaseFileManager::sMutex;
|
|||
DatabaseFileManager::DatabaseFileManager(
|
||||
PersistenceType aPersistenceType,
|
||||
const quota::OriginMetadata& aOriginMetadata,
|
||||
const nsAString& aDatabaseName, const nsCString& aDatabaseID,
|
||||
bool aEnforcingQuota, bool aIsInPrivateBrowsingMode)
|
||||
const nsAString& aDatabaseName, bool aEnforcingQuota)
|
||||
: mPersistenceType(aPersistenceType),
|
||||
mOriginMetadata(aOriginMetadata),
|
||||
mDatabaseName(aDatabaseName),
|
||||
mDatabaseID(aDatabaseID),
|
||||
mEnforcingQuota(aEnforcingQuota),
|
||||
mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {}
|
||||
mEnforcingQuota(aEnforcingQuota) {}
|
||||
|
||||
nsresult DatabaseFileManager::Init(nsIFile* aDirectory,
|
||||
mozIStorageConnection& aConnection) {
|
||||
|
@ -15277,7 +15219,22 @@ nsresult FactoryOp::Open() {
|
|||
MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed);
|
||||
|
||||
if (mInPrivateBrowsing) {
|
||||
gIndexedDBCipherKeyManager->Ensure(mDatabaseId);
|
||||
const auto lockedPrivateBrowsingInfoHashtable =
|
||||
gPrivateBrowsingInfoHashtable->Lock();
|
||||
|
||||
lockedPrivateBrowsingInfoHashtable->LookupOrInsertWith(mDatabaseId, [] {
|
||||
IndexedDBCipherStrategy cipherStrategy;
|
||||
|
||||
// XXX Generate key using proper random data, such that we can ensure
|
||||
// the use of unique IVs per key by discriminating by database's file
|
||||
// id & offset.
|
||||
auto keyOrErr = cipherStrategy.GenerateKey();
|
||||
|
||||
// XXX Propagate the error to the caller rather than asserting.
|
||||
MOZ_RELEASE_ASSERT(keyOrErr.isOk());
|
||||
|
||||
return keyOrErr.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
mState = State::FinishOpen;
|
||||
|
@ -15885,11 +15842,19 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
|
|||
CloneFileAndAppend(*dbDirectory, databaseFilenameBase +
|
||||
kFileManagerDirectoryNameSuffix));
|
||||
|
||||
Maybe<const CipherKey> maybeKey =
|
||||
mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId)
|
||||
: Nothing();
|
||||
Maybe<const CipherKey> maybeKey;
|
||||
if (mInPrivateBrowsing) {
|
||||
CipherKey key;
|
||||
|
||||
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
|
||||
{
|
||||
const auto lockedPrivateBrowsingInfoHashtable =
|
||||
gPrivateBrowsingInfoHashtable->Lock();
|
||||
MOZ_ALWAYS_TRUE(
|
||||
lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key));
|
||||
}
|
||||
|
||||
maybeKey.emplace(std::move(key));
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(
|
||||
NotNull<nsCOMPtr<mozIStorageConnection>> connection,
|
||||
|
@ -15932,8 +15897,7 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
|
|||
|
||||
if (!fileManager) {
|
||||
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
|
||||
persistenceType, mOriginMetadata, databaseName, mDatabaseId,
|
||||
mEnforcingQuota, mInPrivateBrowsing);
|
||||
persistenceType, mOriginMetadata, databaseName, mEnforcingQuota);
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection)));
|
||||
|
||||
|
@ -16593,11 +16557,19 @@ void OpenDatabaseOp::EnsureDatabaseActor() {
|
|||
mMetadata = info->mMetadata.clonePtr();
|
||||
}
|
||||
|
||||
Maybe<const CipherKey> maybeKey =
|
||||
mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId)
|
||||
: Nothing();
|
||||
Maybe<const CipherKey> maybeKey;
|
||||
if (mInPrivateBrowsing) {
|
||||
CipherKey key;
|
||||
|
||||
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
|
||||
{
|
||||
const auto lockedPrivateBrowsingInfoHashtable =
|
||||
gPrivateBrowsingInfoHashtable->Lock();
|
||||
MOZ_ALWAYS_TRUE(
|
||||
lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key));
|
||||
}
|
||||
|
||||
maybeKey.emplace(std::move(key));
|
||||
}
|
||||
|
||||
// XXX Shouldn't Manager() return already_AddRefed when
|
||||
// PBackgroundIDBFactoryParent is declared refcounted?
|
||||
|
@ -16900,11 +16872,16 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto maybeKey = mInPrivateBrowsing
|
||||
? gIndexedDBCipherKeyManager->Get(mDatabaseId)
|
||||
: Nothing();
|
||||
const auto maybeKey = [this]() -> Maybe<const CipherKey> {
|
||||
CipherKey key;
|
||||
|
||||
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
|
||||
const auto lockedPrivateBrowsingInfoHashtable =
|
||||
gPrivateBrowsingInfoHashtable->Lock();
|
||||
if (!lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)) {
|
||||
return Nothing{};
|
||||
}
|
||||
return Some(std::move(key));
|
||||
}();
|
||||
|
||||
// Pass -1 as the directoryLockId to disable quota checking, since we might
|
||||
// temporarily exceed quota before deleting the database.
|
||||
|
@ -17175,11 +17152,6 @@ nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (mDeleteDatabaseOp->mInPrivateBrowsing) {
|
||||
MOZ_ASSERT(
|
||||
gIndexedDBCipherKeyManager->Remove(mDeleteDatabaseOp->mDatabaseId));
|
||||
}
|
||||
|
||||
rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
@ -19555,22 +19527,11 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
|
|||
QM_TRY(OkIf(journalFile), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
|
||||
IDB_REPORT_INTERNAL_ERR_LAMBDA);
|
||||
|
||||
nsCString fileKeyId;
|
||||
fileKeyId.AppendInt(fileInfo.Id());
|
||||
|
||||
const auto maybeKey =
|
||||
Transaction()
|
||||
.GetDatabase()
|
||||
.GetFileManager()
|
||||
.IsInPrivateBrowsingMode()
|
||||
? gIndexedDBCipherKeyManager->Get(
|
||||
Transaction().GetDatabase().Id(), fileKeyId)
|
||||
: Nothing();
|
||||
|
||||
QM_TRY(
|
||||
MOZ_TO_RESULT(fileHelper->CreateFileFromStream(
|
||||
*file, *journalFile, *inputStream,
|
||||
storedFileInfo.ShouldCompress(), maybeKey))
|
||||
storedFileInfo.ShouldCompress(),
|
||||
Transaction().GetDatabase().MaybeKeyRef()))
|
||||
.mapErr([](const nsresult rv) {
|
||||
if (NS_ERROR_GET_MODULE(rv) !=
|
||||
NS_ERROR_MODULE_DOM_INDEXEDDB) {
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "mozilla/dom/indexedDB/Key.h"
|
||||
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
|
||||
#include "IndexedDBCipherKeyManager.h"
|
||||
#include "nscore.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
@ -40,6 +39,9 @@ struct StructuredCloneReadInfoParent;
|
|||
|
||||
extern const nsLiteralString kJournalDirectoryName;
|
||||
|
||||
using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy;
|
||||
using CipherKey = IndexedDBCipherStrategy::KeyType;
|
||||
|
||||
// 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
|
||||
|
|
|
@ -28,13 +28,11 @@ class DatabaseFileManager final
|
|||
const PersistenceType mPersistenceType;
|
||||
const quota::OriginMetadata mOriginMetadata;
|
||||
const nsString mDatabaseName;
|
||||
const nsCString mDatabaseID;
|
||||
|
||||
LazyInitializedOnce<const nsString> mDirectoryPath;
|
||||
LazyInitializedOnce<const nsString> mJournalDirectoryPath;
|
||||
|
||||
const bool mEnforcingQuota;
|
||||
const bool mIsInPrivateBrowsingMode;
|
||||
|
||||
// Lock protecting DatabaseFileManager.mFileInfos.
|
||||
// It's s also used to atomically update DatabaseFileInfo.mRefCnt and
|
||||
|
@ -61,9 +59,7 @@ class DatabaseFileManager final
|
|||
|
||||
DatabaseFileManager(PersistenceType aPersistenceType,
|
||||
const quota::OriginMetadata& aOriginMetadata,
|
||||
const nsAString& aDatabaseName,
|
||||
const nsCString& aDatabaseID, bool aEnforcingQuota,
|
||||
bool aIsInPrivateBrowsingMode);
|
||||
const nsAString& aDatabaseName, bool aEnforcingQuota);
|
||||
|
||||
PersistenceType Type() const { return mPersistenceType; }
|
||||
|
||||
|
@ -74,8 +70,7 @@ class DatabaseFileManager final
|
|||
const nsACString& Origin() const { return mOriginMetadata.mOrigin; }
|
||||
|
||||
const nsAString& DatabaseName() const { return mDatabaseName; }
|
||||
const nsCString& DatabaseID() const { return mDatabaseID; }
|
||||
auto IsInPrivateBrowsingMode() const { return mIsInPrivateBrowsingMode; }
|
||||
|
||||
bool EnforcingQuota() const { return mEnforcingQuota; }
|
||||
|
||||
nsresult Init(nsIFile* aDirectory, mozIStorageConnection& aConnection);
|
||||
|
|
|
@ -1,52 +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/. */
|
||||
|
||||
#ifndef mozilla_dom_indexeddbcipherKeyManager_h
|
||||
#define mozilla_dom_indexeddbcipherKeyManager_h
|
||||
|
||||
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
|
||||
#include "mozilla/DataMutex.h"
|
||||
#include "nsTHashMap.h"
|
||||
|
||||
namespace mozilla::dom::indexedDB {
|
||||
|
||||
namespace {
|
||||
|
||||
using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy;
|
||||
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; any normal key/value pairs get
|
||||
// encrypted using the commmon database key. All keys pertaining to a single
|
||||
// database get stored together using the database id in a hashmap. We are
|
||||
// using a 2-level hashmap here; keys get stored effectively at level 2.
|
||||
// Looking up at level 1 using database id gives us access to hashmap of keys
|
||||
// corresponding to that database which we could use to look up the common
|
||||
// database key and blob keys using "default" and blob file ids respectively.
|
||||
|
||||
public:
|
||||
using PrivateBrowsingInfoHashtable =
|
||||
nsTHashMap<nsCStringHashKey, nsTHashMap<nsCStringHashKey, CipherKey>>;
|
||||
|
||||
IndexedDBCipherKeyManager()
|
||||
: mPrivateBrowsingInfoHashTable("IndexedDBCipherKeyManager"){};
|
||||
Maybe<CipherKey> Get(const nsCString& aDatabaseID,
|
||||
const nsCString& keyStoreID = "default"_ns);
|
||||
CipherKey Ensure(const nsCString& aDatabaseID,
|
||||
const nsCString& keyStoreID = "default"_ns);
|
||||
bool Remove(const nsCString& aDatabaseID);
|
||||
|
||||
private:
|
||||
DataMutex<PrivateBrowsingInfoHashtable> mPrivateBrowsingInfoHashTable;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace mozilla::dom::indexedDB
|
||||
|
||||
#endif // IndexedDBCipherKeyManager_h
|
|
@ -2862,8 +2862,7 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
|
|||
// purpose is to store file ids without adding more complexity or code
|
||||
// duplication.
|
||||
auto fileManager = MakeSafeRefPtr<DatabaseFileManager>(
|
||||
PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, ""_ns, false,
|
||||
false);
|
||||
PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, false);
|
||||
|
||||
nsresult rv = fileManager->Init(aFMDirectory, aConnection);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[DEFAULT]
|
||||
|
||||
[test_IDB_encryption_PBM.py]
|
|
@ -1,139 +0,0 @@
|
|||
# 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/.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from marionette_driver import Wait
|
||||
from marionette_harness import MarionetteTestCase
|
||||
|
||||
INDEXED_DB_PBM_PREF = "dom.indexedDB.privateBrowsing.enabled"
|
||||
|
||||
|
||||
class IDBEncryptionPBM(MarionetteTestCase):
|
||||
|
||||
"""
|
||||
Bug1784966: Ensure IDB data gets encrypted in Private Browsing Mode.
|
||||
We need to ensure data inside both sqlite fields and blob files under
|
||||
*.sqllite gets encrypted.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(IDBEncryptionPBM, self).setUp()
|
||||
|
||||
self.IDBName = "IDBTest"
|
||||
self.IDBStoreName = "IDBTestStore"
|
||||
self.IDBVersion = 1
|
||||
self.IDBValue = "test_IDB_Encryption_PBM"
|
||||
|
||||
self.profilePath = self.marionette.instance.profile.profile
|
||||
|
||||
self.defaultPrefValue = self.marionette.get_pref(INDEXED_DB_PBM_PREF)
|
||||
self.marionette.set_pref(INDEXED_DB_PBM_PREF, True)
|
||||
|
||||
# Navigate by opening a new private window
|
||||
pbmWindowHandle = self.marionette.open(type="window", private=True)["handle"]
|
||||
self.marionette.switch_to_window(pbmWindowHandle)
|
||||
self.marionette.navigate(
|
||||
self.marionette.absolute_url("dom/indexedDB/basicIDB_PBM.html")
|
||||
)
|
||||
|
||||
self.idbStoragePath = self.getIDBStoragePath()
|
||||
|
||||
def tearDown(self):
|
||||
super(IDBEncryptionPBM, self).setUp()
|
||||
self.marionette.set_pref(INDEXED_DB_PBM_PREF, self.defaultPrefValue)
|
||||
|
||||
# closes the new private window we opened in the setUp and referred by 'pbmWindowHandle'
|
||||
self.marionette.close()
|
||||
|
||||
def test_ensure_encrypted_blob(self):
|
||||
self.marionette.execute_script(
|
||||
"""
|
||||
const [idb, store, key, value] = arguments;
|
||||
const blobValue = new Blob([value], {type:'text/plain'});
|
||||
|
||||
window.wrappedJSObject.addDataIntoIDB(idb, store, key, blobValue);
|
||||
""",
|
||||
script_args=(self.IDBName, self.IDBStoreName, "blobKey", self.IDBValue),
|
||||
)
|
||||
|
||||
Wait(self.marionette, timeout=60).until(
|
||||
lambda _: self.sqliteWALReleased(),
|
||||
message="WAL still around even after 60s",
|
||||
)
|
||||
|
||||
idbBlobDir = self.findDirObj(self.idbStoragePath, ".files", False)
|
||||
if idbBlobDir is None:
|
||||
raise Exception(f"unable to find '.files' dir in {self.idbStoragePath}")
|
||||
|
||||
# seems like there's a timing issue here. There are sometimes no blob file
|
||||
# even after WAL is released. Allowing some buffer time and ensuring blob file
|
||||
# exists before validating it's contents
|
||||
idbBlobPath = os.path.join(idbBlobDir, "1")
|
||||
Wait(self.marionette, timeout=10).until(
|
||||
lambda _: os.path.exists(idbBlobPath),
|
||||
message="Blob file does not exist even after waiting 10s",
|
||||
)
|
||||
|
||||
foundRawValue = False
|
||||
with open(idbBlobPath, "rb") as f_binary:
|
||||
foundRawValue = (
|
||||
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
|
||||
)
|
||||
|
||||
self.assertFalse(foundRawValue, "Blob file did not get encrypted")
|
||||
|
||||
def test_ensure_encrpted_sqlite_data(self):
|
||||
self.marionette.execute_script(
|
||||
"""
|
||||
const [idb, store, key, value] = arguments;
|
||||
window.wrappedJSObject.addDataIntoIDB(idb, store, key, value);
|
||||
""",
|
||||
script_args=(self.IDBName, self.IDBStoreName, "textKey", self.IDBValue),
|
||||
)
|
||||
|
||||
Wait(self.marionette, timeout=60).until(
|
||||
lambda _: self.sqliteWALReleased(),
|
||||
message="WAL still around even after 60s",
|
||||
)
|
||||
|
||||
sqliteDBFile = self.findDirObj(self.idbStoragePath, ".sqlite", True)
|
||||
self.assertIsNotNone(
|
||||
sqliteDBFile, f"unable to find .sqlite file in {self.idbStoragePath}"
|
||||
)
|
||||
|
||||
foundRawValue = False
|
||||
with open(sqliteDBFile, "rb") as f_binary:
|
||||
foundRawValue = (
|
||||
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
|
||||
)
|
||||
|
||||
self.assertFalse(foundRawValue, "sqlite data did not get encrypted")
|
||||
|
||||
def getIDBStoragePath(self):
|
||||
origin = (
|
||||
self.marionette.absolute_url("")[:-1].replace(":", "+").replace("/", "+")
|
||||
)
|
||||
|
||||
# origin directory under storage is suffice'd with '^privateBrowsingId=1' for PBM
|
||||
originDir = origin + "^privateBrowsingId=1"
|
||||
|
||||
return os.path.join(self.profilePath, "storage", "default", originDir, "idb")
|
||||
|
||||
def findDirObj(self, path, pattern, isFile):
|
||||
for obj in os.scandir(path):
|
||||
if obj.path.endswith(pattern) and (obj.is_file() == isFile):
|
||||
return obj.path
|
||||
return None
|
||||
|
||||
def sqliteWALReleased(self):
|
||||
"""
|
||||
checks if .sqlite-wal has been cleared or not.
|
||||
returns False if idbStoragePath does not exist
|
||||
"""
|
||||
|
||||
if not os.path.exists(self.idbStoragePath):
|
||||
return False
|
||||
return self.findDirObj(self.idbStoragePath, ".sqlite-wal", True) is None
|
|
@ -797,15 +797,13 @@ nsresult Connection::initialize(nsIFileURL* aFileURL,
|
|||
nsAutoCString query;
|
||||
rv = aFileURL->GetQuery(query);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
const char* const vfs =
|
||||
!URLParams::Parse(query,
|
||||
[](const nsAString& aName, const nsAString& aValue) {
|
||||
return !aName.EqualsLiteral("key");
|
||||
})
|
||||
URLParams::Parse(query,
|
||||
[](const nsAString& aName, const nsAString& aValue) {
|
||||
return aName.EqualsLiteral("key");
|
||||
})
|
||||
? GetObfuscatingVFSName()
|
||||
: GetTelemetryVFSName(exclusive);
|
||||
|
||||
int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, vfs);
|
||||
if (srv != SQLITE_OK) {
|
||||
mDBConn = nullptr;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
# DOM tests
|
||||
[include:../../../../../dom/cache/test/marionette/manifest.ini]
|
||||
[include:../../../../../dom/indexedDB/test/marionette/manifest.ini]
|
||||
[include:../../../../../dom/workers/test/marionette/manifest.ini]
|
||||
|
||||
# browser tests
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<script>
|
||||
async function ensureIDB(name, ver, store) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let createObjectStore = (db, store) => {
|
||||
db.createObjectStore(store);
|
||||
};
|
||||
|
||||
var req = indexedDB.open(name, ver);
|
||||
req.onerror = reject;
|
||||
|
||||
req.onsuccess = (event) => {
|
||||
resolve(req.result);
|
||||
};
|
||||
|
||||
req.onupgradeneeded = function (event) {
|
||||
let db = event.target.result;
|
||||
createObjectStore(db, store);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
async function addDataIntoIDB(idb, store, key, value) {
|
||||
let db = await ensureIDB(idb, 1, store);
|
||||
await (new Promise((resolve, reject) => {
|
||||
var transaction = db.transaction([store], "readwrite");
|
||||
var put = transaction.objectStore(store).put(value, key);
|
||||
put.onsuccess = resolve();
|
||||
}));
|
||||
|
||||
closeIDB(db)
|
||||
};
|
||||
|
||||
function closeIDB(db) {
|
||||
db.close();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче