Skip to content

Commit

Permalink
Fix issue with codec initialization
Browse files Browse the repository at this point in the history
Due to a change in SQLite's behaviour in version 3.48.0 it is no longer possible to locate the cipher configuration table via an SQL function. A named connection parameter is now used for this purpose.
  • Loading branch information
utelle committed Jan 15, 2025
1 parent de01ff8 commit d9767c6
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/cipher_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef struct _CipherName
char m_name[CIPHER_NAME_MAXLEN];
} CipherName;

static char globalConfigTableName[CIPHER_NAME_MAXLEN] = "";
static int globalCipherCount = 0;
static char* globalSentinelName = "";
static CipherName globalCipherNameTable[CODEC_COUNT_LIMIT + 2] = { 0 };
Expand Down
25 changes: 11 additions & 14 deletions src/cipher_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,7 @@ sqlite3mcConfigTable(sqlite3_context* context, int argc, sqlite3_value** argv)
SQLITE_PRIVATE CodecParameter*
sqlite3mcGetCodecParams(sqlite3* db)
{
CodecParameter* codecParams = NULL;
sqlite3_stmt* pStmt = 0;
int rc = sqlite3_prepare_v2(db, "SELECT sqlite3mc_config_table();", -1, &pStmt, 0);
if (rc == SQLITE_OK)
{
if (SQLITE_ROW == sqlite3_step(pStmt))
{
sqlite3_value* ptrValue = sqlite3_column_value(pStmt, 0);
codecParams = (CodecParameter*) sqlite3_value_pointer(ptrValue, "sqlite3mc_codec_params");
}
sqlite3_finalize(pStmt);
}
CodecParameter* codecParams = (CodecParameter*) sqlite3_get_clientdata(db, globalConfigTableName);
return codecParams;
}

Expand Down Expand Up @@ -909,8 +898,16 @@ sqlite3mcFileControlPragma(sqlite3* db, const char* zDbName, int op, void* pArg)
{
value = sqlite3mc_config(db, "cipher", cipherId);
}
rc = SQLITE_OK;
((char**)pArg)[0] = sqlite3_mprintf("%s", globalCodecDescriptorTable[value - 1].m_name);
if (value > 0)
{
((char**)pArg)[0] = sqlite3_mprintf("%s", globalCodecDescriptorTable[value - 1].m_name);
rc = SQLITE_OK;
}
else
{
((char**)pArg)[0] = sqlite3_mprintf("Cipher '%s' could not be located.", pragmaValue);
rc = SQLITE_ERROR;
}
}
else
{
Expand Down
3 changes: 2 additions & 1 deletion src/codecext.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ sqlite3_key_v2(sqlite3* db, const char* zDbName, const void* zKey, int nKey)
return rc;
}
/* Configure cipher from URI parameters if requested */
if (sqlite3FindFunction(db, "sqlite3mc_config_table", 0, SQLITE_UTF8, 0) == NULL)
void* codecParamTable = sqlite3_get_clientdata(db, globalConfigTableName);
if (codecParamTable == NULL)
{
/*
** Encryption extension of database connection not yet initialized;
Expand Down
15 changes: 12 additions & 3 deletions src/sqlite3mc.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,8 @@ mcRegisterCodecExtensions(sqlite3* db, char** pzErrMsg, const sqlite3_api_routin
int rc = SQLITE_OK;
CodecParameter* codecParameterTable = NULL;

if (sqlite3FindFunction(db, "sqlite3mc_config_table", 1, SQLITE_UTF8, 0) != NULL)
void* codecParamTable = sqlite3_get_clientdata(db, globalConfigTableName);
if (codecParamTable)
{
/* Return if codec extension functions are already defined */
return rc;
Expand All @@ -392,8 +393,7 @@ mcRegisterCodecExtensions(sqlite3* db, char** pzErrMsg, const sqlite3_api_routin
rc = (codecParameterTable != NULL) ? SQLITE_OK : SQLITE_NOMEM;
if (rc == SQLITE_OK)
{
rc = sqlite3_create_function_v2(db, "sqlite3mc_config_table", 0, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
codecParameterTable, sqlite3mcConfigTable, 0, 0, sqlite3mcFreeCodecParameterTable);
sqlite3_set_clientdata(db, globalConfigTableName, codecParameterTable, sqlite3mcFreeCodecParameterTable);
}

rc = (codecParameterTable != NULL) ? SQLITE_OK : SQLITE_NOMEM;
Expand Down Expand Up @@ -610,6 +610,15 @@ sqlite3mcInitCipherTables()
{
size_t n;

/* Initialize global configuration table name */
sqlite3_randomness(CIPHER_NAME_MAXLEN, globalConfigTableName);
for (n = 0; n < CIPHER_NAME_MAXLEN-1; ++n)
{
if (globalConfigTableName[n] == 0)
globalConfigTableName[n] = '@';
}
globalConfigTableName[CIPHER_NAME_MAXLEN-1] = 0;

/* Initialize cipher name table */
strcpy(globalCipherNameTable[0].m_name, "global");
for (n = 1; n < CODEC_COUNT_MAX + 2; ++n)
Expand Down

2 comments on commit d9767c6

@05nelsonm
Copy link

@05nelsonm 05nelsonm commented on d9767c6 Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean SQL Functions, as described in The Docs, no longer work?

I'm getting failure to open errors when updating from 2.0.1 to 2.0.2 now.

EDIT: I switched from using SELECT sqlite3mc_config(...) to PRAGMA {temp/main}.{name} = {value} and everything is working fine now.

@utelle
Copy link
Owner Author

@utelle utelle commented on d9767c6 Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean SQL Functions, as described in The Docs, no longer work?

Yes and no.

Yes: The SQL configuration functions can't be used any longer for configuring the cipher scheme before issuing the PRAGMA key statement. The reason is that starting with SQLite 3.48.0 executing a SELECT statement will now always access the underlying database file. For encrypted databases this will fail, because the cipher scheme is not yet activated.

No: However, after the cipher scheme was activated successfully with PRAGMA key the SQL configuration functions can be used, for example, to configure a cipher scheme for attaching encrypted databases.

I'm getting failure to open errors when updating from 2.0.1 to 2.0.2 now.

EDIT: I switched from using SELECT sqlite3mc_config(...) to PRAGMA {temp/main}.{name} = {value} and everything is working fine now.

Yes, using PRAGMA statements to configure the cipher scheme is the way to go. An alternative is to specify the cipher configuration via URI parameters.

The SQL functions for configuring the cipher scheme exist mostly for historical reasons. For SQLite versions below 3.32.0 the SQLite code contained stubs for the encryption API - no need to patch the SQLite code, but also no way to handle own PRAGMA statements. Back then using SELECT with user-defined SQL functions was the only way to configure the cipher scheme without having to patch the SQLite code.

I will have to update the documentation accordingly. Actually, the change in SQLite's behaviour caught me off guard. I hadn't expected that suddenly schema-independent SQL functions would not work anymore for configuring the encryption extension. In the SQLite 3.48.0 release notes there was no hint about such a change.

Please sign in to comment.