diff --git a/packaging/android/Android.mk b/packaging/android/Android.mk index ddcb9b8a..b03a1fde 100644 --- a/packaging/android/Android.mk +++ b/packaging/android/Android.mk @@ -12,7 +12,6 @@ LOCAL_CFLAGS += -DHAVE_USLEEP=1 LOCAL_CFLAGS += -DSQLITE_ENABLE_FTS5 LOCAL_CFLAGS += -DSQLITE_ENABLE_RTREE LOCAL_CFLAGS += -DSQLITE_ENABLE_FTS3 -LOCAL_CFLAGS += -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE # Select default encryption scheme # ChaCha20-Poly1305 is the default diff --git a/src/cipher_common.c b/src/cipher_common.c index 38e0fba8..40f42b5b 100644 --- a/src/cipher_common.c +++ b/src/cipher_common.c @@ -688,7 +688,11 @@ SQLITE_PRIVATE unsigned char* mcReadDatabaseHeader(Codec* codec, unsigned char* sqlite3_int64 fileSize = 0; int ret = sqlite3OsFileSize(fd, &fileSize); if (fileSize == 0) + { + /* Empty file: there is no salt yet, a random salt will be generated */ *rc = SQLITE_OK; + return NULL; + } } if (*rc == SQLITE_OK) hdr = dbHeader; diff --git a/src/memory_secure.c b/src/memory_secure.c index 937149d6..adb99c49 100644 --- a/src/memory_secure.c +++ b/src/memory_secure.c @@ -209,16 +209,26 @@ SQLITE_PRIVATE void sqlite3mcInitMemoryMethods() #if SQLITE3MC_SECURE_MEMORY if (!mcSecureMemoryInitialized) { - if (sqlite3_config(SQLITE_CONFIG_GETMALLOC, &mcDefaultMemoryMethods) == SQLITE_OK) + sqlite3_mem_methods currentMemoryMethods; + if (sqlite3_config(SQLITE_CONFIG_GETMALLOC, ¤tMemoryMethods) == SQLITE_OK) { - mcSecureMemoryMethods.pAppData = mcDefaultMemoryMethods.pAppData; - if (sqlite3_config(SQLITE_CONFIG_MALLOC, &mcSecureMemoryMethods) == SQLITE_OK) + if (currentMemoryMethods.xMalloc == mcSecureMemoryMethods.xMalloc) { + /* Still installed after sqlite3_shutdown(), do not wrap it a second time */ mcSecureMemoryInitialized = 1; } else { - mcSecureMemoryFlag = mcSecureMemoryInitialized = 0; + mcDefaultMemoryMethods = currentMemoryMethods; + mcSecureMemoryMethods.pAppData = mcDefaultMemoryMethods.pAppData; + if (sqlite3_config(SQLITE_CONFIG_MALLOC, &mcSecureMemoryMethods) == SQLITE_OK) + { + mcSecureMemoryInitialized = 1; + } + else + { + mcSecureMemoryFlag = mcSecureMemoryInitialized = 0; + } } } else diff --git a/src/sqlite3mc_vfs.c b/src/sqlite3mc_vfs.c index a7182096..3e8f4da7 100644 --- a/src/sqlite3mc_vfs.c +++ b/src/sqlite3mc_vfs.c @@ -723,8 +723,8 @@ static sqlite3mc_file* mcFindDbMainFileName(sqlite3mc_vfs* mcVfs, const char* zF static sqlite3mc_vfs* mcFindVfs(sqlite3* db, const char* zDbName) { int dbIndex = (zDbName) ? sqlite3FindDbName(db, zDbName) : 0; - Pager* pager = sqlite3BtreePager(db->aDb[dbIndex].pBt); - sqlite3_vfs* dbVfs = pager->pVfs; + Btree* pBt = (dbIndex >= 0) ? db->aDb[dbIndex].pBt : NULL; + sqlite3_vfs* dbVfs = (pBt != NULL) ? sqlite3BtreePager(pBt)->pVfs : NULL; sqlite3mc_vfs* pVfsMC = NULL; if (dbVfs && dbVfs->xOpen == mcVfsOpen) diff --git a/test/rekeytest.c b/test/rekeytest.c index 4eb8adef..62a241e3 100644 --- a/test/rekeytest.c +++ b/test/rekeytest.c @@ -21,7 +21,8 @@ ** always be supplied (rekey, failure case, plain -> encrypted) ** Part 8 : salt management (random salt, salt in memory across several rekeys, ** rekey_salt=on/off) and behaviour when reading the file header -** (unencrypted/empty/too short file, I/O error while reading the header) +** (unencrypted/empty/too short file, I/O error while reading the header), +** key, rekey and backup for a schema name without a database file ** ** Invariant in parts 3 and 6 (after every rekey, whether it failed or not): ** exactly ONE of the two states (old/new) is completely readable @@ -1765,6 +1766,25 @@ static void t8_short_header(void) rc = open_state("chacha20", "k", &db, &n); CHECK(rc == SQLITE_OK && n == 1, "reopened and readable without cipher_salt"); if (db) sqlite3_close(db); + + /* A second new DB with the same key must get a different salt */ + { + unsigned char h2[16]; + remove_files(); + write_bytes(DBFILE, 0); + rc = sqlite3_open_v2(DBFILE, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "multipleciphers-faulty"); + if (rc == SQLITE_OK) + { + set_params(db, "chacha20"); + keyRc = sqlite3_key(db, "k", 1); + rc = sqlite3_exec(db, "CREATE TABLE t(a INTEGER PRIMARY KEY, b TEXT)", 0, 0, 0); + if (keyRc != SQLITE_OK) rc = keyRc; + } + sqlite3_close(db); + db = 0; + CHECK(rc == SQLITE_OK && read_header_salt(h2) && memcmp(h, h2, 16) != 0, + "second empty file + same key: different salt"); + } } for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) @@ -1827,6 +1847,33 @@ static void t8_header_read_error(void) CHECK(state_ok("chacha20", "old-key", g_nrows), "afterwards completely readable with the correct key"); } +/* Key, rekey and backup for a schema name without a database file +** (unknown name, "temp" before it is used): an error, no crash */ +static void t8_schema_without_file(void) +{ + sqlite3* db = 0; + sqlite3* dbDest = 0; + sqlite3_backup* backup; + int rc; + + printf("\n== key, rekey and backup for a schema without database file ==\n"); + snprintf(g_ctx, sizeof(g_ctx), "Part 8: schema_without_file"); + remove_files(); + rc = sqlite3_open_v2(DBFILE, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "multipleciphers-faulty"); + CHECK(rc == SQLITE_OK, "DB opened"); + if (rc != SQLITE_OK) return; + CHECK(sqlite3_key_v2(db, "nosuchdb", "k", 1) != SQLITE_OK, "key for an unknown schema fails"); + CHECK(sqlite3_key_v2(db, "temp", "k", 1) != SQLITE_OK, "key for temp fails"); + CHECK(sqlite3_rekey_v2(db, "nosuchdb", "k", 1) != SQLITE_OK, "rekey for an unknown schema fails"); + CHECK(sqlite3_rekey_v2(db, "temp", "k", 1) != SQLITE_OK, "rekey for temp fails"); + rc = sqlite3_open(":memory:", &dbDest); + backup = (rc == SQLITE_OK) ? sqlite3_backup_init(dbDest, "main", db, "temp") : NULL; + if (backup) sqlite3_backup_finish(backup); + CHECK(rc == SQLITE_OK, "backup from temp returns without a crash"); + sqlite3_close(dbDest); + sqlite3_close(db); +} + static void run_part8(void) { t8_random_salt(); @@ -1837,6 +1884,7 @@ static void run_part8(void) t8_plain_with_key(); t8_short_header(); t8_header_read_error(); + t8_schema_without_file(); } /* ------------------------------------------------------------------ */