set error state if cipher_migrate fails

This commit is contained in:
Stephen Lombardo 2021-10-08 16:50:57 -04:00
parent 99d36b40c8
commit 78796fd0e2
6 changed files with 186 additions and 49 deletions

View File

@ -111,18 +111,39 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
} else
#endif
#ifdef SQLCIPHER_TEST
if( sqlite3StrICmp(zLeft,"cipher_test")==0 ){
if( sqlite3StrICmp(zLeft,"cipher_test_on")==0 ){
if( zRight ) {
if(sqlite3StrICmp(zRight, "fail_next_encrypt")) {
sqlcipher_set_test_flags(sqlcipher_get_test_flags() ^ TEST_FAIL_NEXT_ENCRYPT);
unsigned int flags = sqlcipher_get_test_flags();
if(sqlite3StrICmp(zRight, "fail_encrypt")==0) {
flags |= TEST_FAIL_ENCRYPT;
} else
if(sqlite3StrICmp(zRight, "fail_next_decrypt")) {
sqlcipher_set_test_flags(sqlcipher_get_test_flags() ^ TEST_FAIL_NEXT_DECRYPT);
}
} else {
char *flags = sqlite3_mprintf("%d", sqlcipher_get_test_flags());
codec_vdbe_return_string(pParse, "cipher_test", flags, P4_DYNAMIC);
if(sqlite3StrICmp(zRight, "fail_decrypt")==0) {
flags |= TEST_FAIL_DECRYPT;
} else
if(sqlite3StrICmp(zRight, "fail_migrate")==0) {
flags |= TEST_FAIL_MIGRATE;
}
sqlcipher_set_test_flags(flags);
}
} else
if( sqlite3StrICmp(zLeft,"cipher_test_off")==0 ){
if( zRight ) {
unsigned int flags = sqlcipher_get_test_flags();
if(sqlite3StrICmp(zRight, "fail_encrypt")==0) {
flags &= ~TEST_FAIL_ENCRYPT;
} else
if(sqlite3StrICmp(zRight, "fail_decrypt")==0) {
flags &= ~TEST_FAIL_DECRYPT;
} else
if(sqlite3StrICmp(zRight, "fail_migrate")==0) {
flags &= ~TEST_FAIL_MIGRATE;
}
sqlcipher_set_test_flags(flags);
}
} else
if( sqlite3StrICmp(zLeft,"cipher_test")==0 ){
char *flags = sqlite3_mprintf("%i", sqlcipher_get_test_flags());
codec_vdbe_return_string(pParse, "cipher_test", flags, P4_DYNAMIC);
}else
#endif
if( sqlite3StrICmp(zLeft, "cipher_fips_status")== 0 && !zRight ){
@ -157,8 +178,12 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
} else
if( sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){
if(ctx){
char *migrate_status = sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx));
int status = sqlcipher_codec_ctx_migrate(ctx);
char *migrate_status = sqlite3_mprintf("%d", status);
codec_vdbe_return_string(pParse, "cipher_migrate", migrate_status, P4_DYNAMIC);
if(status != SQLITE_OK) {
sqlcipher_codec_ctx_set_error(ctx, status);
}
}
} else
if( sqlite3StrICmp(zLeft, "cipher_provider")==0 && !zRight ){
@ -709,7 +734,7 @@ static void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_DECRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset);
#ifdef SQLCIPHER_TEST
if((sqlcipher_get_test_flags() & TEST_FAIL_NEXT_ENCRYPT) > 0) rc = SQLITE_ERROR;
if((sqlcipher_get_test_flags() & TEST_FAIL_ENCRYPT) > 0) rc = SQLITE_ERROR;
#endif
if(rc != SQLITE_OK) { /* clear results of failed cipher operation and set error */
sqlcipher_memset((unsigned char*) buffer+offset, 0, page_sz-offset);
@ -734,7 +759,7 @@ static void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
}
rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset);
#ifdef SQLCIPHER_TEST
if((sqlcipher_get_test_flags() & TEST_FAIL_NEXT_DECRYPT) > 0) rc = SQLITE_ERROR;
if((sqlcipher_get_test_flags() & TEST_FAIL_DECRYPT) > 0) rc = SQLITE_ERROR;
#endif
if(rc != SQLITE_OK) { /* clear results of failed cipher operation and set error */
sqlcipher_memset((unsigned char*)buffer+offset, 0, page_sz-offset);

View File

@ -193,10 +193,11 @@ static int cipher_isHex(const unsigned char *hex, int sz){
/* possible flags for simulating specific test conditions */
#ifdef SQLCIPHER_TEST
#define TEST_FAIL_NEXT_ENCRYPT (1ul << 0) /* 1 */
#define TEST_FAIL_NEXT_DECRYPT (1ul << 1) /* 2 */
int sqlcipher_get_test_flags(void);
void sqlcipher_set_test_flags(int);
#define TEST_FAIL_ENCRYPT 0x01
#define TEST_FAIL_DECRYPT 0x02
#define TEST_FAIL_MIGRATE 0x04
unsigned int sqlcipher_get_test_flags(void);
void sqlcipher_set_test_flags(unsigned int);
#endif
/* extensions defined in crypto_impl.c */

View File

@ -45,7 +45,13 @@
#endif
#ifdef SQLCIPHER_TEST
static volatile int cipher_test_flags = 0;
static volatile unsigned int cipher_test_flags = 0;
unsigned int sqlcipher_get_test_flags() {
return cipher_test_flags;
}
void sqlcipher_set_test_flags(unsigned int flags) {
cipher_test_flags = flags;
}
#endif
/* Generate code to return a string value */
@ -402,16 +408,6 @@ char* sqlcipher_version() {
return version;
}
#ifdef SQLCIPHER_TEST
int sqlcipher_get_test_flags() {
return cipher_test_flags;
}
void sqlcipher_set_test_flags(int flags) {
cipher_test_flags = flags;
}
#endif
/**
* Initialize new cipher_ctx struct. This function will allocate memory
* for the cipher context and for the key
@ -1380,7 +1376,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
pass = sqlcipher_malloc(pass_sz+1);
memset(pass, 0, pass_sz+1);
memcpy(pass, ctx->read_ctx->pass, pass_sz);
/* Version 4 - current, no upgrade required, so exit immediately */
rc = sqlcipher_check_connection(db_filename, pass, pass_sz, "", &user_version, &journal_mode);
if(rc == SQLITE_OK){
@ -1398,6 +1394,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
if(pragma_compat) sqlcipher_free(pragma_compat, sqlite3Strlen30(pragma_compat));
pragma_compat = NULL;
}
/* if we exit the loop normally we failed to determine the version, this is an error */
CODEC_TRACE("Upgrade format not determined\n");
goto handle_error;
@ -1445,6 +1442,14 @@ migrate:
goto handle_error;
}
#ifdef SQLCIPHER_TEST
if((sqlcipher_get_test_flags() & TEST_FAIL_MIGRATE) > 0) {
rc = SQLITE_ERROR;
CODEC_TRACE("simulated migrate failure, error code %d\n", rc);
goto handle_error;
}
#endif
rc = sqlite3_exec(db, set_user_version, NULL, NULL, NULL);
if(rc != SQLITE_OK){
CODEC_TRACE("set user version failed, error code %d\n", rc);
@ -1537,7 +1542,6 @@ migrate:
handle_error:
CODEC_TRACE("An error occurred attempting to migrate the database - last error %d\n", rc);
rc = SQLITE_ERROR;
cleanup:
if(pass) sqlcipher_free(pass, pass_sz);

View File

@ -66,7 +66,7 @@ do_test codec-error-journal-delete {
catchsql {
PRAGMA key = 'testkey';
PRAGMA cipher_test = fail_next_encrypt;
PRAGMA cipher_test_on = fail_encrypt;
UPDATE t1 SET b = 'fail' WHERE a = 5000;
}
@ -74,7 +74,7 @@ do_test codec-error-journal-delete {
sqlite_orig db test.db
execsql {
PRAGMA cipher_test = fail_next_encrypt;
PRAGMA cipher_test_off = fail_encrypt;
PRAGMA key = 'testkey';
PRAGMA cipher_integrity_check;
PRAGMA integrity_check;
@ -92,7 +92,7 @@ do_test codec-error-journal-wal {
catchsql {
PRAGMA key = 'testkey';
PRAGMA cipher_test = fail_next_encrypt;
PRAGMA cipher_test_on = fail_encrypt;
UPDATE t1 SET b = 'fail' WHERE a = 5000;
}
@ -100,7 +100,7 @@ do_test codec-error-journal-wal {
sqlite_orig db test.db
execsql {
PRAGMA cipher_test = fail_next_encrypt;
PRAGMA cipher_test_off = fail_encrypt;
PRAGMA key = 'testkey';
PRAGMA cipher_integrity_check;
PRAGMA integrity_check;
@ -120,7 +120,7 @@ do_test codec-error-journal-wal-transaction {
PRAGMA key = 'testkey';
BEGIN;
UPDATE t1 SET b = 'success' WHERE a = 1;
PRAGMA cipher_test = fail_next_encrypt;
PRAGMA cipher_test_on = fail_encrypt;
UPDATE t1 SET b = 'fail' WHERE a = 5000;
COMMIT;
}
@ -129,7 +129,7 @@ do_test codec-error-journal-wal-transaction {
sqlite_orig db test.db
execsql {
PRAGMA cipher_test = fail_next_encrypt;
PRAGMA cipher_test_off = fail_encrypt;
PRAGMA key = 'testkey';
PRAGMA cipher_integrity_check;
PRAGMA integrity_check;
@ -149,7 +149,7 @@ do_test codec-error-journal-wal-read {
catchsql {
PRAGMA key = 'testkey';
SELECT count(*) FROM sqlite_schema;
PRAGMA cipher_test = fail_next_decrypt;
PRAGMA cipher_test_on = fail_decrypt;
UPDATE t1 SET b = 'fail' WHERE a = 5000;
}
@ -157,7 +157,7 @@ do_test codec-error-journal-wal-read {
sqlite_orig db test.db
execsql {
PRAGMA cipher_test = fail_next_decrypt;
PRAGMA cipher_test_off = fail_decrypt;
PRAGMA key = 'testkey';
PRAGMA cipher_integrity_check;
PRAGMA integrity_check;

View File

@ -360,7 +360,8 @@ file delete -force test.db
# open a 1.1.8 database without hmac, then copy the data
do_test attach-and-copy-1.1.8 {
sqlite_orig db $sampleDir/sqlcipher-1.1.8-testkey.db
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db test.db
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@ -368,14 +369,14 @@ do_test attach-and-copy-1.1.8 {
PRAGMA kdf_iter = 4000;
PRAGMA cipher_page_size = 1024;
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey-hmac';
ATTACH DATABASE 'test-new.db' AS db2 KEY 'testkey-hmac';
CREATE TABLE db2.t1(a,b);
INSERT INTO db2.t1 SELECT * FROM main.t1;
DETACH DATABASE db2;
}
db close
sqlite_orig db test.db
sqlite_orig db test-new.db
execsql {
PRAGMA key = 'testkey-hmac';
SELECT count(*) FROM t1;
@ -384,7 +385,7 @@ do_test attach-and-copy-1.1.8 {
} {ok 75709 1 1 one one 1 2 one two 1 2}
db close
file delete -force test.db
file delete -force test-new.db
# open a standard database, then attach a new
# database with completely different options.
@ -681,7 +682,7 @@ file delete -force test-vacuum.db
# setting as the original
do_test default-hmac-kdf-attach {
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db test.db
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db;
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db
sqlite_orig db test.db
execsql {
PRAGMA cipher_default_use_hmac = OFF;
@ -707,7 +708,7 @@ file delete -force sqlcipher-1.1.8-testkey.db
# fail because the hmac setting for the
# attached database is not compatible
do_test attach-1.1.8-database-from-2.0-fails {
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db;
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db
sqlite_orig db test.db
catchsql {
PRAGMA key = 'testkey';
@ -725,7 +726,7 @@ file delete -force sqlcipher-1.1.8-testkey.db
# succeed now that hmac is off by default
# before the attach
do_test change-default-hmac-kdf-attach {
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db;
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@ -1087,19 +1088,22 @@ file delete -force test.db test.db-migrated test.db-journal
do_test migrate-3-0-database-to-current-format {
file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db
sqlite_orig db test.db
execsql {
set rc {}
lappend rc [execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_migrate;
}
SELECT count(*) FROM sqlite_schema;
}]
db close
sqlite_orig db test.db
execsql {
lappend rc [execsql {
PRAGMA key = 'testkey';
SELECT count(*) FROM sqlite_schema;
PRAGMA journal_mode;
}
} {ok 1 delete}
}]
} {{ok 0 1} {ok 1 delete}}
db close
file delete -force test.db
@ -1133,6 +1137,58 @@ do_test migrate-wal-database-to-current {
db close
file delete -force test.db
# test original database is left untouched after
# a failed migration e.g. due to low disk space
do_test migrate-failure {
file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db
sqlite_orig db test.db
set rc {}
lappend rc [execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_test_on = fail_migrate;
PRAGMA cipher_migrate;
}]
db close
sqlite_orig db test.db
lappend rc [execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_test_off = fail_migrate;
PRAGMA cipher_compatibility = 3;
SELECT count(*) FROM sqlite_schema;
}]
} {{ok 1} {ok 1}}
db close
file delete -force test.db test.db-migrated
# leave database is a readable state after a
# a failed migration
do_test migrate-failure-readable {
file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db
sqlite_orig db test.db
set rc {}
lappend rc [execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_test_on = fail_migrate;
PRAGMA cipher_migrate;
}]
lappend rc [catchsql {
SELECT count(*) FROM sqlite_schema;
}]
db close
sqlite_orig db test.db
lappend rc [execsql {
PRAGMA cipher_test_off = fail_migrate;
PRAGMA cipher_test;
}]
} {{ok 1} {1 {SQL logic error}} 0}
db close
file delete -force test.db test.db-migrated
do_test key-database-by-name {
sqlite_orig db test.db
@ -1259,6 +1315,7 @@ do_test can-migrate-with-raw-hex-key {
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
PRAGMA cipher_migrate;
}
db close
sqlite_orig db test.db
execsql {

View File

@ -801,4 +801,54 @@ db close
dba close
file delete -force test.db
do_test test_flags_fail_encrypt {
sqlite_orig db :memory:
execsql {
PRAGMA cipher_test;
PRAGMA cipher_test_on = fail_encrypt;
PRAGMA cipher_test;
PRAGMA cipher_test_off = fail_encrypt;
PRAGMA cipher_test;
}
} {0 1 0}
db close
do_test test_flags_fail_decrypt {
sqlite_orig db :memory:
execsql {
PRAGMA cipher_test;
PRAGMA cipher_test_on = fail_decrypt;
PRAGMA cipher_test;
PRAGMA cipher_test_off = fail_decrypt;
PRAGMA cipher_test;
}
} {0 2 0}
db close
do_test test_flags_fail_migrate {
sqlite_orig db :memory:
execsql {
PRAGMA cipher_test;
PRAGMA cipher_test_on = fail_migrate;
PRAGMA cipher_test;
PRAGMA cipher_test_off = fail_migrate;
PRAGMA cipher_test;
}
} {0 4 0}
db close
do_test test_flags_combo {
sqlite_orig db :memory:
execsql {
PRAGMA cipher_test;
PRAGMA cipher_test_on = fail_encrypt;
PRAGMA cipher_test_on = fail_migrate;
PRAGMA cipher_test;
PRAGMA cipher_test_off = fail_encrypt;
PRAGMA cipher_test_off = fail_migrate;
PRAGMA cipher_test;
}
} {0 5 0}
db close
finish_test