diff options
Diffstat (limited to 'storage/ObfuscatingVFS.cpp')
-rw-r--r-- | storage/ObfuscatingVFS.cpp | 691 |
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 |