Fossil SCM
Additional defenses to prevent the db_protect() and db_protect_pop() SQL functions from being misused.
Commit
6ade35427f4651f0df50335bc90dbc6d81448e24f00a92466d55a6554049ca5a
Parent
3cb82905e105cc0…
1 file changed
+37
-13
+37
-13
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -32,10 +32,15 @@ | ||
| 32 | 32 | |
| 33 | 33 | #ifndef _WIN32 |
| 34 | 34 | # include "linenoise.h" |
| 35 | 35 | #endif |
| 36 | 36 | |
| 37 | +/* | |
| 38 | +** True if the "fossil sql" command has the --test flag. False otherwise. | |
| 39 | +*/ | |
| 40 | +static int local_bSqlCmdTest = 0; | |
| 41 | + | |
| 37 | 42 | /* |
| 38 | 43 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 39 | 44 | ** content of artifact identified by X as a blob. |
| 40 | 45 | */ |
| 41 | 46 | static void sqlcmd_content( |
| @@ -163,33 +168,46 @@ | ||
| 163 | 168 | ** Undocumented test SQL functions: |
| 164 | 169 | ** |
| 165 | 170 | ** db_protect(X) |
| 166 | 171 | ** db_protect_pop(X) |
| 167 | 172 | ** |
| 168 | -** These invoke the corresponding C routines. Misuse may result in | |
| 169 | -** an assertion fault. | |
| 173 | +** These invoke the corresponding C routines. | |
| 174 | +** | |
| 175 | +** WARNING: | |
| 176 | +** Do not instantiate these functions for any Fossil webpage or command | |
| 177 | +** method of than the "fossil sql" command. If an attacker gains access | |
| 178 | +** to these functions, he will be able to disable other defense mechanisms. | |
| 179 | +** | |
| 180 | +** This routines are for interactiving testing only. They are experimental | |
| 181 | +** and undocumented (apart from this comments) and might go away or change | |
| 182 | +** in future releases. | |
| 183 | +** | |
| 184 | +** 2020-11-29: This functions are now only available if the "fossil sql" | |
| 185 | +** command is started with the --test option. | |
| 170 | 186 | */ |
| 171 | 187 | static void sqlcmd_db_protect( |
| 172 | 188 | sqlite3_context *context, |
| 173 | 189 | int argc, |
| 174 | 190 | sqlite3_value **argv |
| 175 | 191 | ){ |
| 176 | 192 | unsigned mask = 0; |
| 177 | 193 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 178 | - if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 179 | - if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 180 | - if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 181 | - if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 182 | - if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 183 | - db_protect(mask); | |
| 194 | + if( z!=0 && local_bSqlCmdTest ){ | |
| 195 | + if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 196 | + if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 197 | + if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 198 | + if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 199 | + if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 200 | + db_protect(mask); | |
| 201 | + } | |
| 184 | 202 | } |
| 185 | 203 | static void sqlcmd_db_protect_pop( |
| 186 | 204 | sqlite3_context *context, |
| 187 | 205 | int argc, |
| 188 | 206 | sqlite3_value **argv |
| 189 | 207 | ){ |
| 190 | - db_protect_pop(); | |
| 208 | + if( !local_bSqlCmdTest ) db_protect_pop(); | |
| 191 | 209 | } |
| 192 | 210 | |
| 193 | 211 | |
| 194 | 212 | |
| 195 | 213 | |
| @@ -232,14 +250,16 @@ | ||
| 232 | 250 | ** will get cleaned up when the shell closes the database connection */ |
| 233 | 251 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 234 | 252 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 235 | 253 | db_protect_only(PROTECT_NONE); |
| 236 | 254 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 237 | - sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 238 | - sqlcmd_db_protect, 0, 0); | |
| 239 | - sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 240 | - sqlcmd_db_protect_pop, 0, 0); | |
| 255 | + if( local_bSqlCmdTest ){ | |
| 256 | + sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 257 | + sqlcmd_db_protect, 0, 0); | |
| 258 | + sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 259 | + sqlcmd_db_protect_pop, 0, 0); | |
| 260 | + } | |
| 241 | 261 | return SQLITE_OK; |
| 242 | 262 | } |
| 243 | 263 | |
| 244 | 264 | /* |
| 245 | 265 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +349,13 @@ | ||
| 329 | 349 | ** --readonly Open the repository read-only. No changes |
| 330 | 350 | ** are allowed. This is a recommended safety |
| 331 | 351 | ** precaution to prevent repository damage. |
| 332 | 352 | ** |
| 333 | 353 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 354 | +** | |
| 355 | +** --test Enable some testing and analysis features | |
| 356 | +** that are normally disabled. | |
| 334 | 357 | ** |
| 335 | 358 | ** All of the standard sqlite3 command-line shell options should also |
| 336 | 359 | ** work. |
| 337 | 360 | ** |
| 338 | 361 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +420,11 @@ | ||
| 397 | 420 | extern int sqlite3_shell(int, char**); |
| 398 | 421 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 399 | 422 | g.fNoThHook = 1; |
| 400 | 423 | #endif |
| 401 | 424 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 425 | + local_bSqlCmdTest = find_option("test",0,0)!=0; | |
| 402 | 426 | if( !noRepository ){ |
| 403 | 427 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 404 | 428 | } |
| 405 | 429 | db_open_config(1,0); |
| 406 | 430 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 407 | 431 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -32,10 +32,15 @@ | |
| 32 | |
| 33 | #ifndef _WIN32 |
| 34 | # include "linenoise.h" |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 39 | ** content of artifact identified by X as a blob. |
| 40 | */ |
| 41 | static void sqlcmd_content( |
| @@ -163,33 +168,46 @@ | |
| 163 | ** Undocumented test SQL functions: |
| 164 | ** |
| 165 | ** db_protect(X) |
| 166 | ** db_protect_pop(X) |
| 167 | ** |
| 168 | ** These invoke the corresponding C routines. Misuse may result in |
| 169 | ** an assertion fault. |
| 170 | */ |
| 171 | static void sqlcmd_db_protect( |
| 172 | sqlite3_context *context, |
| 173 | int argc, |
| 174 | sqlite3_value **argv |
| 175 | ){ |
| 176 | unsigned mask = 0; |
| 177 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 178 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 179 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 180 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 181 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 182 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 183 | db_protect(mask); |
| 184 | } |
| 185 | static void sqlcmd_db_protect_pop( |
| 186 | sqlite3_context *context, |
| 187 | int argc, |
| 188 | sqlite3_value **argv |
| 189 | ){ |
| 190 | db_protect_pop(); |
| 191 | } |
| 192 | |
| 193 | |
| 194 | |
| 195 | |
| @@ -232,14 +250,16 @@ | |
| 232 | ** will get cleaned up when the shell closes the database connection */ |
| 233 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 234 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 235 | db_protect_only(PROTECT_NONE); |
| 236 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 237 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 238 | sqlcmd_db_protect, 0, 0); |
| 239 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 240 | sqlcmd_db_protect_pop, 0, 0); |
| 241 | return SQLITE_OK; |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +349,13 @@ | |
| 329 | ** --readonly Open the repository read-only. No changes |
| 330 | ** are allowed. This is a recommended safety |
| 331 | ** precaution to prevent repository damage. |
| 332 | ** |
| 333 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 334 | ** |
| 335 | ** All of the standard sqlite3 command-line shell options should also |
| 336 | ** work. |
| 337 | ** |
| 338 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +420,11 @@ | |
| 397 | extern int sqlite3_shell(int, char**); |
| 398 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 399 | g.fNoThHook = 1; |
| 400 | #endif |
| 401 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 402 | if( !noRepository ){ |
| 403 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 404 | } |
| 405 | db_open_config(1,0); |
| 406 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 407 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -32,10 +32,15 @@ | |
| 32 | |
| 33 | #ifndef _WIN32 |
| 34 | # include "linenoise.h" |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** True if the "fossil sql" command has the --test flag. False otherwise. |
| 39 | */ |
| 40 | static int local_bSqlCmdTest = 0; |
| 41 | |
| 42 | /* |
| 43 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 44 | ** content of artifact identified by X as a blob. |
| 45 | */ |
| 46 | static void sqlcmd_content( |
| @@ -163,33 +168,46 @@ | |
| 168 | ** Undocumented test SQL functions: |
| 169 | ** |
| 170 | ** db_protect(X) |
| 171 | ** db_protect_pop(X) |
| 172 | ** |
| 173 | ** These invoke the corresponding C routines. |
| 174 | ** |
| 175 | ** WARNING: |
| 176 | ** Do not instantiate these functions for any Fossil webpage or command |
| 177 | ** method of than the "fossil sql" command. If an attacker gains access |
| 178 | ** to these functions, he will be able to disable other defense mechanisms. |
| 179 | ** |
| 180 | ** This routines are for interactiving testing only. They are experimental |
| 181 | ** and undocumented (apart from this comments) and might go away or change |
| 182 | ** in future releases. |
| 183 | ** |
| 184 | ** 2020-11-29: This functions are now only available if the "fossil sql" |
| 185 | ** command is started with the --test option. |
| 186 | */ |
| 187 | static void sqlcmd_db_protect( |
| 188 | sqlite3_context *context, |
| 189 | int argc, |
| 190 | sqlite3_value **argv |
| 191 | ){ |
| 192 | unsigned mask = 0; |
| 193 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 194 | if( z!=0 && local_bSqlCmdTest ){ |
| 195 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 196 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 197 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 198 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 199 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 200 | db_protect(mask); |
| 201 | } |
| 202 | } |
| 203 | static void sqlcmd_db_protect_pop( |
| 204 | sqlite3_context *context, |
| 205 | int argc, |
| 206 | sqlite3_value **argv |
| 207 | ){ |
| 208 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 209 | } |
| 210 | |
| 211 | |
| 212 | |
| 213 | |
| @@ -232,14 +250,16 @@ | |
| 250 | ** will get cleaned up when the shell closes the database connection */ |
| 251 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 252 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 253 | db_protect_only(PROTECT_NONE); |
| 254 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 255 | if( local_bSqlCmdTest ){ |
| 256 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 257 | sqlcmd_db_protect, 0, 0); |
| 258 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 259 | sqlcmd_db_protect_pop, 0, 0); |
| 260 | } |
| 261 | return SQLITE_OK; |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +349,13 @@ | |
| 349 | ** --readonly Open the repository read-only. No changes |
| 350 | ** are allowed. This is a recommended safety |
| 351 | ** precaution to prevent repository damage. |
| 352 | ** |
| 353 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 354 | ** |
| 355 | ** --test Enable some testing and analysis features |
| 356 | ** that are normally disabled. |
| 357 | ** |
| 358 | ** All of the standard sqlite3 command-line shell options should also |
| 359 | ** work. |
| 360 | ** |
| 361 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +420,11 @@ | |
| 420 | extern int sqlite3_shell(int, char**); |
| 421 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 422 | g.fNoThHook = 1; |
| 423 | #endif |
| 424 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 425 | local_bSqlCmdTest = find_option("test",0,0)!=0; |
| 426 | if( !noRepository ){ |
| 427 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 428 | } |
| 429 | db_open_config(1,0); |
| 430 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 431 |