summaryrefslogtreecommitdiffstats
path: root/storage/ObfuscatingVFS.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /storage/ObfuscatingVFS.cpp
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/ObfuscatingVFS.cpp')
-rw-r--r--storage/ObfuscatingVFS.cpp691
1 files changed, 691 insertions, 0 deletions
diff --git a/storage/ObfuscatingVFS.cpp b/storage/ObfuscatingVFS.cpp
new file mode 100644
index 0000000000..bba04f560f
--- /dev/null
+++ b/storage/ObfuscatingVFS.cpp
@@ -0,0 +1,691 @@
+/*
+** 2020-04-20
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file implements a VFS shim that obfuscates database content
+** written to disk by applying a CipherStrategy.
+**
+** COMPILING
+**
+** This extension requires SQLite 3.32.0 or later.
+**
+**
+** LOADING
+**
+** Initialize it using a single API call as follows:
+**
+** sqlite3_obfsvfs_init();
+**
+** Obfsvfs is a VFS Shim. When loaded, "obfsvfs" becomes the new
+** default VFS and it uses the prior default VFS as the next VFS
+** down in the stack. This is normally what you want. However, it
+** complex situations where multiple VFS shims are being loaded,
+** it might be important to ensure that obfsvfs is loaded in the
+** correct order so that it sequences itself into the default VFS
+** Shim stack in the right order.
+**
+** USING
+**
+** Open database connections using the sqlite3_open_v2() with
+** the SQLITE_OPEN_URI flag and using a URI filename that includes
+** the query parameter "key=XXXXXXXXXXX..." where the XXXX... consists
+** of 64 hexadecimal digits (32 bytes of content).
+**
+** Create a new encrypted database by opening a file that does not
+** yet exist using the key= query parameter.
+**
+** LIMITATIONS:
+**
+** * An obfuscated database must be created as such. There is
+** no way to convert an existing database file into an
+** obfuscated database file other than to run ".dump" on the
+** older database and reimport the SQL text into a new
+** obfuscated database.
+**
+** * There is no way to change the key value, other than to
+** ".dump" and restore the database
+**
+** * The database page size must be exactly 8192 bytes. No other
+** database page sizes are currently supported.
+**
+** * Memory-mapped I/O does not work for obfuscated databases.
+** If you think about it, memory-mapped I/O doesn't make any
+** sense for obfuscated databases since you have to make a
+** copy of the content to deobfuscate anyhow - you might as
+** well use normal read()/write().
+**
+** * Only the main database, the rollback journal, and WAL file
+** are obfuscated. Other temporary files used for things like
+** SAVEPOINTs or as part of a large external sort remain
+** unobfuscated.
+**
+** * Requires SQLite 3.32.0 or later.
+*/
+#include "sqlite3.h"
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h> /* For debugging only */
+
+#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
+#include "mozilla/ScopeExit.h"
+#include "nsPrintfCString.h"
+
+/*
+** Forward declaration of objects used by this utility
+*/
+using ObfsVfs = sqlite3_vfs;
+
+/*
+** Useful datatype abbreviations
+*/
+#if !defined(SQLITE_CORE)
+using u8 = unsigned char;
+#endif
+
+/* Access to a lower-level VFS that (might) implement dynamic loading,
+** access to randomness, etc.
+*/
+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
+#define ORIGFILE(p) ((sqlite3_file*)(((ObfsFile*)(p)) + 1))
+
+/*
+** Database page size for obfuscated databases
+*/
+#define OBFS_PGSZ 8192
+
+#define WAL_FRAMEHDRSIZE 24
+
+using namespace mozilla;
+using namespace mozilla::dom::quota;
+
+/* An open file */
+struct ObfsFile {
+ sqlite3_file base; /* IO methods */
+ const char* zFName; /* Original name of the file */
+ bool inCkpt; /* Currently doing a checkpoint */
+ ObfsFile* pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
+ void* pTemp; /* Temporary storage for encoded pages */
+ IPCStreamCipherStrategy*
+ encryptCipherStrategy; /* CipherStrategy for encryption */
+ IPCStreamCipherStrategy*
+ decryptCipherStrategy; /* CipherStrategy for decryption */
+};
+
+/*
+** Methods for ObfsFile
+*/
+static int obfsClose(sqlite3_file*);
+static int obfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int obfsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
+static int obfsTruncate(sqlite3_file*, sqlite3_int64 size);
+static int obfsSync(sqlite3_file*, int flags);
+static int obfsFileSize(sqlite3_file*, sqlite3_int64* pSize);
+static int obfsLock(sqlite3_file*, int);
+static int obfsUnlock(sqlite3_file*, int);
+static int obfsCheckReservedLock(sqlite3_file*, int* pResOut);
+static int obfsFileControl(sqlite3_file*, int op, void* pArg);
+static int obfsSectorSize(sqlite3_file*);
+static int obfsDeviceCharacteristics(sqlite3_file*);
+static int obfsShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
+static int obfsShmLock(sqlite3_file*, int offset, int n, int flags);
+static void obfsShmBarrier(sqlite3_file*);
+static int obfsShmUnmap(sqlite3_file*, int deleteFlag);
+static int obfsFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void** pp);
+static int obfsUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void* p);
+
+/*
+** Methods for ObfsVfs
+*/
+static int obfsOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
+static int obfsDelete(sqlite3_vfs*, const char* zPath, int syncDir);
+static int obfsAccess(sqlite3_vfs*, const char* zPath, int flags, int*);
+static int obfsFullPathname(sqlite3_vfs*, const char* zPath, int, char* zOut);
+static void* obfsDlOpen(sqlite3_vfs*, const char* zPath);
+static void obfsDlError(sqlite3_vfs*, int nByte, char* zErrMsg);
+static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void);
+static void obfsDlClose(sqlite3_vfs*, void*);
+static int obfsRandomness(sqlite3_vfs*, int nByte, char* zBufOut);
+static int obfsSleep(sqlite3_vfs*, int nMicroseconds);
+static int obfsCurrentTime(sqlite3_vfs*, double*);
+static int obfsGetLastError(sqlite3_vfs*, int, char*);
+static int obfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+static int obfsSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
+static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs*, const char* z);
+static const char* obfsNextSystemCall(sqlite3_vfs*, const char* zName);
+
+static const sqlite3_io_methods obfs_io_methods = {
+ 3, /* iVersion */
+ obfsClose, /* xClose */
+ obfsRead, /* xRead */
+ obfsWrite, /* xWrite */
+ obfsTruncate, /* xTruncate */
+ obfsSync, /* xSync */
+ obfsFileSize, /* xFileSize */
+ obfsLock, /* xLock */
+ obfsUnlock, /* xUnlock */
+ obfsCheckReservedLock, /* xCheckReservedLock */
+ obfsFileControl, /* xFileControl */
+ obfsSectorSize, /* xSectorSize */
+ obfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ obfsShmMap, /* xShmMap */
+ obfsShmLock, /* xShmLock */
+ obfsShmBarrier, /* xShmBarrier */
+ obfsShmUnmap, /* xShmUnmap */
+ obfsFetch, /* xFetch */
+ obfsUnfetch /* xUnfetch */
+};
+
+static constexpr int kKeyBytes = 32;
+static constexpr int kIvBytes = IPCStreamCipherStrategy::BlockPrefixLength;
+static constexpr int kClearTextPrefixBytesOnFirstPage = 32;
+static constexpr int kReservedBytes = 32;
+static constexpr int kBasicBlockSize = IPCStreamCipherStrategy::BasicBlockSize;
+static_assert(kClearTextPrefixBytesOnFirstPage % kBasicBlockSize == 0);
+static_assert(kReservedBytes % kBasicBlockSize == 0);
+
+/* Obfuscate a page using p->encryptCipherStrategy.
+**
+** A new random nonce is created and stored in the last 32 bytes
+** of the page. All other bytes of the page are obfuscasted using the
+** CipherStrategy. Except, for page-1 (including the SQLite
+** database header) the first 32 bytes are not obfuscated
+**
+** Return a pointer to the obfuscated content, which is held in the
+** p->pTemp buffer. Or return a NULL pointer if something goes wrong.
+** Errors are reported using NS_WARNING().
+*/
+static void* obfsEncode(ObfsFile* p, /* File containing page to be obfuscated */
+ u8* a, /* database page to be obfuscated */
+ int nByte /* Bytes of content in a[]. Must be a multiple
+ of kBasicBlockSize. */
+) {
+ u8 aIv[kIvBytes];
+ u8* pOut;
+ int i;
+
+ static_assert((kIvBytes & (kIvBytes - 1)) == 0);
+ sqlite3_randomness(kIvBytes, aIv);
+ pOut = (u8*)p->pTemp;
+ if (pOut == nullptr) {
+ pOut = static_cast<u8*>(sqlite3_malloc64(nByte));
+ if (pOut == nullptr) {
+ NS_WARNING(nsPrintfCString("unable to allocate a buffer in which to"
+ " write obfuscated database content for %s",
+ p->zFName)
+ .get());
+ return nullptr;
+ }
+ p->pTemp = pOut;
+ }
+ if (memcmp(a, "SQLite format 3", 16) == 0) {
+ i = kClearTextPrefixBytesOnFirstPage;
+ if (a[20] != kReservedBytes) {
+ NS_WARNING(nsPrintfCString("obfuscated database must have reserved-bytes"
+ " set to %d",
+ kReservedBytes)
+ .get());
+ return nullptr;
+ }
+ memcpy(pOut, a, kClearTextPrefixBytesOnFirstPage);
+ } else {
+ i = 0;
+ }
+ const int payloadLength = nByte - kReservedBytes - i;
+ MOZ_ASSERT(payloadLength > 0);
+ // XXX I guess this can be done in-place as well, then we don't need the
+ // temporary page at all, I guess?
+ p->encryptCipherStrategy->Cipher(
+ Span{aIv}, Span{a + i, static_cast<unsigned>(payloadLength)},
+ Span{pOut + i, static_cast<unsigned>(payloadLength)});
+ memcpy(pOut + nByte - kReservedBytes, aIv, kIvBytes);
+
+ return pOut;
+}
+
+/* De-obfuscate a page using p->decryptCipherStrategy.
+**
+** The deobfuscation is done in-place.
+**
+** For pages that begin with the SQLite header text, the first
+** 32 bytes are not deobfuscated.
+*/
+static void obfsDecode(ObfsFile* p, /* File containing page to be obfuscated */
+ u8* a, /* database page to be obfuscated */
+ int nByte /* Bytes of content in a[]. Must be a multiple
+ of kBasicBlockSize. */
+) {
+ int i;
+
+ if (memcmp(a, "SQLite format 3", 16) == 0) {
+ i = kClearTextPrefixBytesOnFirstPage;
+ } else {
+ i = 0;
+ }
+ const int payloadLength = nByte - kReservedBytes - i;
+ MOZ_ASSERT(payloadLength > 0);
+ p->decryptCipherStrategy->Cipher(
+ Span{a + nByte - kReservedBytes, kIvBytes},
+ Span{a + i, static_cast<unsigned>(payloadLength)},
+ Span{a + i, static_cast<unsigned>(payloadLength)});
+ memset(a + nByte - kReservedBytes, 0, kIvBytes);
+}
+
+/*
+** Close an obfsucated file.
+*/
+static int obfsClose(sqlite3_file* pFile) {
+ ObfsFile* p = (ObfsFile*)pFile;
+ if (p->pPartner) {
+ MOZ_ASSERT(p->pPartner->pPartner == p);
+ p->pPartner->pPartner = nullptr;
+ p->pPartner = nullptr;
+ }
+ sqlite3_free(p->pTemp);
+
+ delete p->decryptCipherStrategy;
+ delete p->encryptCipherStrategy;
+
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xClose(pFile);
+}
+
+/*
+** Read data from an obfuscated file.
+**
+** If the file is less than one full page in length, then return
+** a substitute "prototype" page-1. This prototype page one
+** specifies a database in WAL mode with an 8192-byte page size
+** and a 32-byte reserved-bytes value. Those settings are necessary
+** for obfuscation to function correctly.
+*/
+static int obfsRead(sqlite3_file* pFile, void* zBuf, int iAmt,
+ sqlite_int64 iOfst) {
+ int rc;
+ ObfsFile* p = (ObfsFile*)pFile;
+ pFile = ORIGFILE(pFile);
+ rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
+ if (rc == SQLITE_OK) {
+ if ((iAmt == OBFS_PGSZ || iAmt == OBFS_PGSZ + WAL_FRAMEHDRSIZE) &&
+ !p->inCkpt) {
+ obfsDecode(p, ((u8*)zBuf) + iAmt - OBFS_PGSZ, OBFS_PGSZ);
+ }
+ } else if (rc == SQLITE_IOERR_SHORT_READ && iOfst == 0 && iAmt >= 100) {
+ static const unsigned char aEmptyDb[] = {
+ // Offset 0, Size 16, The header string: "SQLite format 3\000"
+ 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+ 0x74, 0x20, 0x33, 0x00,
+ // XXX Add description for other fields
+ 0x20, 0x00, 0x02, 0x02, kReservedBytes, 0x40, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ // Offset 52, Size 4, The page number of the largest root b-tree page
+ // when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
+ 0x00, 0x00, 0x00, 0x01};
+
+ memcpy(zBuf, aEmptyDb, sizeof(aEmptyDb));
+ memset(((u8*)zBuf) + sizeof(aEmptyDb), 0, iAmt - sizeof(aEmptyDb));
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Write data to an obfuscated file or journal.
+*/
+static int obfsWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
+ sqlite_int64 iOfst) {
+ ObfsFile* p = (ObfsFile*)pFile;
+ pFile = ORIGFILE(pFile);
+ if (iAmt == OBFS_PGSZ && !p->inCkpt) {
+ zBuf = obfsEncode(p, (u8*)zBuf, iAmt);
+ if (zBuf == nullptr) {
+ return SQLITE_IOERR;
+ }
+ }
+ return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
+}
+
+/*
+** Truncate an obfuscated file.
+*/
+static int obfsTruncate(sqlite3_file* pFile, sqlite_int64 size) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xTruncate(pFile, size);
+}
+
+/*
+** Sync an obfuscated file.
+*/
+static int obfsSync(sqlite3_file* pFile, int flags) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xSync(pFile, flags);
+}
+
+/*
+** Return the current file-size of an obfuscated file.
+*/
+static int obfsFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
+ ObfsFile* p = (ObfsFile*)pFile;
+ pFile = ORIGFILE(p);
+ return pFile->pMethods->xFileSize(pFile, pSize);
+}
+
+/*
+** Lock an obfuscated file.
+*/
+static int obfsLock(sqlite3_file* pFile, int eLock) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xLock(pFile, eLock);
+}
+
+/*
+** Unlock an obfuscated file.
+*/
+static int obfsUnlock(sqlite3_file* pFile, int eLock) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xUnlock(pFile, eLock);
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an obfuscated file.
+*/
+static int obfsCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
+}
+
+/*
+** File control method. For custom operations on an obfuscated file.
+*/
+static int obfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
+ int rc;
+ ObfsFile* p = (ObfsFile*)pFile;
+ pFile = ORIGFILE(pFile);
+ if (op == SQLITE_FCNTL_PRAGMA) {
+ char** azArg = (char**)pArg;
+ MOZ_ASSERT(azArg[1] != nullptr);
+ if (azArg[2] != nullptr && sqlite3_stricmp(azArg[1], "page_size") == 0) {
+ /* Do not allow page size changes on an obfuscated database */
+ return SQLITE_OK;
+ }
+ } else if (op == SQLITE_FCNTL_CKPT_START || op == SQLITE_FCNTL_CKPT_DONE) {
+ p->inCkpt = op == SQLITE_FCNTL_CKPT_START;
+ if (p->pPartner) {
+ p->pPartner->inCkpt = p->inCkpt;
+ }
+ }
+ rc = pFile->pMethods->xFileControl(pFile, op, pArg);
+ if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) {
+ *(char**)pArg = sqlite3_mprintf("obfs/%z", *(char**)pArg);
+ }
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an obfuscated file.
+*/
+static int obfsSectorSize(sqlite3_file* pFile) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xSectorSize(pFile);
+}
+
+/*
+** Return the device characteristic flags supported by an obfuscated file.
+*/
+static int obfsDeviceCharacteristics(sqlite3_file* pFile) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xDeviceCharacteristics(pFile);
+}
+
+/* Create a shared memory file mapping */
+static int obfsShmMap(sqlite3_file* pFile, int iPg, int pgsz, int bExtend,
+ void volatile** pp) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xShmMap(pFile, iPg, pgsz, bExtend, pp);
+}
+
+/* Perform locking on a shared-memory segment */
+static int obfsShmLock(sqlite3_file* pFile, int offset, int n, int flags) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xShmLock(pFile, offset, n, flags);
+}
+
+/* Memory barrier operation on shared memory */
+static void obfsShmBarrier(sqlite3_file* pFile) {
+ pFile = ORIGFILE(pFile);
+ pFile->pMethods->xShmBarrier(pFile);
+}
+
+/* Unmap a shared memory segment */
+static int obfsShmUnmap(sqlite3_file* pFile, int deleteFlag) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xShmUnmap(pFile, deleteFlag);
+}
+
+/* Fetch a page of a memory-mapped file */
+static int obfsFetch(sqlite3_file* pFile, sqlite3_int64 iOfst, int iAmt,
+ void** pp) {
+ *pp = nullptr;
+ return SQLITE_OK;
+}
+
+/* Release a memory-mapped page */
+static int obfsUnfetch(sqlite3_file* pFile, sqlite3_int64 iOfst, void* pPage) {
+ pFile = ORIGFILE(pFile);
+ return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
+}
+
+/*
+** Translate a single byte of Hex into an integer.
+** This routine only works if h really is a valid hexadecimal
+** character: 0..9a..fA..F
+*/
+static u8 obfsHexToInt(int h) {
+ MOZ_ASSERT((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') ||
+ (h >= 'A' && h <= 'F'));
+#if 1 /* ASCII */
+ h += 9 * (1 & (h >> 6));
+#else /* EBCDIC */
+ h += 9 * (1 & ~(h >> 4));
+#endif
+ return (u8)(h & 0xf);
+}
+
+/*
+** Open a new file.
+**
+** If the file is an ordinary database file, or a rollback or WAL journal
+** file, and if the key=XXXX parameter exists, then try to open the file
+** as an obfuscated database. All other open attempts fall through into
+** the lower-level VFS shim.
+**
+** If the key=XXXX parameter exists but is not 64-bytes of hex key, then
+** put an error message in NS_WARNING() and return SQLITE_CANTOPEN.
+*/
+static int obfsOpen(sqlite3_vfs* pVfs, const char* zName, sqlite3_file* pFile,
+ int flags, int* pOutFlags) {
+ ObfsFile* p;
+ sqlite3_file* pSubFile;
+ sqlite3_vfs* pSubVfs;
+ int rc, i;
+ const char* zKey;
+ u8 aKey[kKeyBytes];
+ pSubVfs = ORIGVFS(pVfs);
+ if (flags &
+ (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
+ zKey = sqlite3_uri_parameter(zName, "key");
+ } else {
+ zKey = nullptr;
+ }
+ if (zKey == nullptr) {
+ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
+ }
+ for (i = 0;
+ i < kKeyBytes && isxdigit(zKey[i * 2]) && isxdigit(zKey[i * 2 + 1]);
+ i++) {
+ aKey[i] = (obfsHexToInt(zKey[i * 2]) << 4) | obfsHexToInt(zKey[i * 2 + 1]);
+ }
+ if (i != kKeyBytes) {
+ NS_WARNING(
+ nsPrintfCString("invalid query parameter on %s: key=%s", zName, zKey)
+ .get());
+ return SQLITE_CANTOPEN;
+ }
+ p = (ObfsFile*)pFile;
+ memset(p, 0, sizeof(*p));
+
+ auto encryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
+ auto decryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
+
+ auto resetMethods = MakeScopeExit([pFile] { pFile->pMethods = nullptr; });
+
+ if (NS_WARN_IF(NS_FAILED(encryptCipherStrategy->Init(
+ CipherMode::Encrypt, Span{aKey, sizeof(aKey)},
+ IPCStreamCipherStrategy::MakeBlockPrefix())))) {
+ return SQLITE_ERROR;
+ }
+
+ if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy->Init(
+ CipherMode::Decrypt, Span{aKey, sizeof(aKey)})))) {
+ return SQLITE_ERROR;
+ }
+
+ pSubFile = ORIGFILE(pFile);
+ p->base.pMethods = &obfs_io_methods;
+ rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
+ if (rc) {
+ return rc;
+ }
+
+ resetMethods.release();
+
+ if (flags & (SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
+ sqlite3_file* pDb = sqlite3_database_file_object(zName);
+ p->pPartner = (ObfsFile*)pDb;
+ MOZ_ASSERT(p->pPartner->pPartner == nullptr);
+ p->pPartner->pPartner = p;
+ }
+ p->zFName = zName;
+
+ p->encryptCipherStrategy = encryptCipherStrategy.release();
+ p->decryptCipherStrategy = decryptCipherStrategy.release();
+
+ return SQLITE_OK;
+}
+
+/*
+** All other VFS methods are pass-thrus.
+*/
+static int obfsDelete(sqlite3_vfs* pVfs, const char* zPath, int syncDir) {
+ return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, syncDir);
+}
+static int obfsAccess(sqlite3_vfs* pVfs, const char* zPath, int flags,
+ int* pResOut) {
+ return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
+}
+static int obfsFullPathname(sqlite3_vfs* pVfs, const char* zPath, int nOut,
+ char* zOut) {
+ return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs), zPath, nOut, zOut);
+}
+static void* obfsDlOpen(sqlite3_vfs* pVfs, const char* zPath) {
+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
+}
+static void obfsDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) {
+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
+}
+static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void) {
+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
+}
+static void obfsDlClose(sqlite3_vfs* pVfs, void* pHandle) {
+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
+}
+static int obfsRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) {
+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
+}
+static int obfsSleep(sqlite3_vfs* pVfs, int nMicroseconds) {
+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicroseconds);
+}
+static int obfsCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) {
+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
+}
+static int obfsGetLastError(sqlite3_vfs* pVfs, int a, char* b) {
+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
+}
+static int obfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64* p) {
+ return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
+}
+static int obfsSetSystemCall(sqlite3_vfs* pVfs, const char* zName,
+ sqlite3_syscall_ptr pCall) {
+ return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs), zName, pCall);
+}
+static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs* pVfs,
+ const char* zName) {
+ return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs), zName);
+}
+static const char* obfsNextSystemCall(sqlite3_vfs* pVfs, const char* zName) {
+ return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
+}
+
+namespace mozilla {
+namespace storage {
+
+const char* GetObfuscatingVFSName() { return "obfsvfs"; }
+
+UniquePtr<sqlite3_vfs> ConstructObfuscatingVFS(const char* aBaseVFSName) {
+ MOZ_ASSERT(aBaseVFSName);
+
+ if (sqlite3_vfs_find(GetObfuscatingVFSName()) != nullptr) {
+ return nullptr;
+ }
+ sqlite3_vfs* const pOrig = sqlite3_vfs_find(aBaseVFSName);
+ if (pOrig == nullptr) {
+ return nullptr;
+ }
+
+#ifdef DEBUG
+ // If the VFS version is higher than the last known one, you should update
+ // this VFS adding appropriate methods for any methods added in the version
+ // change.
+ static constexpr int kLastKnownVfsVersion = 3;
+ MOZ_ASSERT(pOrig->iVersion <= kLastKnownVfsVersion);
+#endif
+
+ const sqlite3_vfs obfs_vfs = {
+ pOrig->iVersion, /* iVersion */
+ static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
+ pOrig->mxPathname, /* mxPathname */
+ nullptr, /* pNext */
+ GetObfuscatingVFSName(), /* zName */
+ pOrig, /* pAppData */
+ obfsOpen, /* xOpen */
+ obfsDelete, /* xDelete */
+ obfsAccess, /* xAccess */
+ obfsFullPathname, /* xFullPathname */
+ obfsDlOpen, /* xDlOpen */
+ obfsDlError, /* xDlError */
+ obfsDlSym, /* xDlSym */
+ obfsDlClose, /* xDlClose */
+ obfsRandomness, /* xRandomness */
+ obfsSleep, /* xSleep */
+ obfsCurrentTime, /* xCurrentTime */
+ obfsGetLastError, /* xGetLastError */
+ obfsCurrentTimeInt64, /* xCurrentTimeInt64 */
+ obfsSetSystemCall, /* xSetSystemCall */
+ obfsGetSystemCall, /* xGetSystemCall */
+ obfsNextSystemCall /* xNextSystemCall */
+ };
+
+ return MakeUnique<sqlite3_vfs>(obfs_vfs);
+}
+
+} // namespace storage
+} // namespace mozilla