Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Fix the new read-only-repo security mechanism so that it enables write access when necessary. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
f8363db81b52e7c5cf98961378d48505 |
| User & Date: | drh 2022-12-29 19:39:41.245 |
Context
|
2022-12-29
| ||
| 19:49 | Only apply the PROTECT_READONLY restriction to the "repository", "configdb", and "localdb" database files. check-in: b4e00621e3 user: drh tags: trunk | |
| 19:39 | Fix the new read-only-repo security mechanism so that it enables write access when necessary. check-in: f8363db81b user: drh tags: trunk | |
| 18:56 | Add messages to the error log if the authorizer blocks an SQL statement for security reasons. This change requires a bug fix in SQLite and so it also includes the latest trunk version of SQLite. check-in: 3d8bb63aab user: drh tags: trunk | |
Changes
Changes to src/db.c.
| ︙ | ︙ | |||
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
&& g.repositoryOpen
){
/* Create the triggers needed to protect sensitive settings from
** being created or modified the first time that PROTECT_SENSITIVE
** is enabled. Deleting a sensitive setting is harmless, so there
** is not trigger to block deletes. After being created once, the
** triggers persist for the life of the database connection. */
db_multi_exec(
"CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config"
" WHEN protected_setting(new.name) BEGIN"
" SELECT raise(abort,'not authorized');"
"END;\n"
"CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config"
" WHEN protected_setting(new.name) BEGIN"
" SELECT raise(abort,'not authorized');"
"END;\n"
);
db.bProtectTriggers = 1;
}
db.protectMask = flags;
}
void db_protect(unsigned flags){
db_protect_only(db.protectMask | flags);
}
void db_unprotect(unsigned flags){
if( db.nProtect>=count(db.aProtect)-2 ){
fossil_panic("too many db_unprotect() calls");
}
db.aProtect[db.nProtect++] = db.protectMask;
db.protectMask &= ~flags;
}
void db_protect_pop(void){
if( db.nProtect<1 ){
fossil_panic("too many db_protect_pop() calls");
}
db.protectMask = db.aProtect[--db.nProtect];
}
/*
** Verify that the desired database write protections are in place.
** Throw a fatal error if not.
*/
void db_assert_protected(unsigned flags){
if( (flags & db.protectMask)!=flags ){
| > > > > > > | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
&& g.repositoryOpen
){
/* Create the triggers needed to protect sensitive settings from
** being created or modified the first time that PROTECT_SENSITIVE
** is enabled. Deleting a sensitive setting is harmless, so there
** is not trigger to block deletes. After being created once, the
** triggers persist for the life of the database connection. */
unsigned savedProtectMask = db.protectMask;
db.protectMask = 0;
db_multi_exec(
"CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config"
" WHEN protected_setting(new.name) BEGIN"
" SELECT raise(abort,'not authorized');"
"END;\n"
"CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config"
" WHEN protected_setting(new.name) BEGIN"
" SELECT raise(abort,'not authorized');"
"END;\n"
);
db.bProtectTriggers = 1;
db.protectMask = savedProtectMask;
}
db.protectMask = flags;
}
void db_protect(unsigned flags){
db_protect_only(db.protectMask | flags);
}
void db_unprotect(unsigned flags){
if( db.nProtect>=count(db.aProtect)-2 ){
fossil_panic("too many db_unprotect() calls");
}
db.aProtect[db.nProtect++] = db.protectMask;
db.protectMask &= ~flags;
}
void db_protect_pop(void){
if( db.nProtect<1 ){
fossil_panic("too many db_protect_pop() calls");
}
db.protectMask = db.aProtect[--db.nProtect];
}
int db_is_protected(unsigned flags){
return (db.protectMask & flags)!=0;
}
/*
** Verify that the desired database write protections are in place.
** Throw a fatal error if not.
*/
void db_assert_protected(unsigned flags){
if( (flags & db.protectMask)!=flags ){
|
| ︙ | ︙ | |||
534 535 536 537 538 539 540 |
sqlite3_stricmp(z0,"global_config")==0 ){
fossil_errorlog(
"SECURITY: authorizer blocks DML on protected GLOBAL_CONFIG table\n");
rc = SQLITE_DENY;
}else if( (db.protectMask & PROTECT_READONLY)!=0
&& sqlite3_stricmp(z2,"temp")!=0 ){
fossil_errorlog(
| | | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
sqlite3_stricmp(z0,"global_config")==0 ){
fossil_errorlog(
"SECURITY: authorizer blocks DML on protected GLOBAL_CONFIG table\n");
rc = SQLITE_DENY;
}else if( (db.protectMask & PROTECT_READONLY)!=0
&& sqlite3_stricmp(z2,"temp")!=0 ){
fossil_errorlog(
"SECURITY: authorizer blocks DML on table \"%s\" due to the "
"request coming from a different origin\n", z0);
rc = SQLITE_DENY;
}
break;
}
case SQLITE_DROP_TEMP_TRIGGER: {
/* Do not allow the triggers that enforce PROTECT_SENSITIVE
|
| ︙ | ︙ | |||
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 |
g.zAuxSchema = db_get("aux-schema","");
g.eHashPolicy = db_get_int("hash-policy",-1);
if( g.eHashPolicy<0 ){
g.eHashPolicy = hname_default_policy();
db_set_int("hash-policy", g.eHashPolicy, 0);
}
/* Make a change to the CHECK constraint on the BLOB table for
** version 2.0 and later.
*/
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
/* Additional checks that occur when opening the check-out database */
if( g.localOpen ){
/* If the repository database that was just opened has been
** eplaced by a clone of the same project, with different RID
** values, then renumber the RID values stored in various tables
| > > | 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 |
g.zAuxSchema = db_get("aux-schema","");
g.eHashPolicy = db_get_int("hash-policy",-1);
if( g.eHashPolicy<0 ){
g.eHashPolicy = hname_default_policy();
db_set_int("hash-policy", g.eHashPolicy, 0);
}
#if 0 /* No longer automatic. Need to run "fossil rebuild" to migrate */
/* Make a change to the CHECK constraint on the BLOB table for
** version 2.0 and later.
*/
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
#endif
/* Additional checks that occur when opening the check-out database */
if( g.localOpen ){
/* If the repository database that was just opened has been
** eplaced by a clone of the same project, with different RID
** values, then renumber the RID values stored in various tables
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
648 649 650 651 652 653 654 |
/*
** Look for a file named zName in the check-in with RID=vid. Load the content
** of that file into pContent and return the RID for the file. Or return 0
** if the file is not found or could not be loaded.
*/
int doc_load_content(int vid, const char *zName, Blob *pContent){
| | > > > > > > > | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 |
/*
** Look for a file named zName in the check-in with RID=vid. Load the content
** of that file into pContent and return the RID for the file. Or return 0
** if the file is not found or could not be loaded.
*/
int doc_load_content(int vid, const char *zName, Blob *pContent){
int writable;
int rid; /* The RID of the file being loaded */
if( db_is_protected(PROTECT_READONLY)
|| !db_is_writeable("repository")
){
writable = 0;
}else{
writable = 1;
}
if( writable ){
db_end_transaction(0);
db_begin_write();
}
if( !db_table_exists("repository", "vcache") || !writable ){
db_multi_exec(
"CREATE %s TABLE IF NOT EXISTS vcache(\n"
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 |
int allowRepoList /* Send repo list for "/" URL */
){
const char *zPathInfo = PD("PATH_INFO", "");
char *zPath = NULL;
int i;
const CmdOrPage *pCmd = 0;
const char *zBase = g.zRepositoryName;
g.zPhase = "process_one_web_page";
#if !defined(_WIN32)
signal(SIGSEGV, sigsegv_handler);
#endif
/* Handle universal query parameters */
| > | 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 |
int allowRepoList /* Send repo list for "/" URL */
){
const char *zPathInfo = PD("PATH_INFO", "");
char *zPath = NULL;
int i;
const CmdOrPage *pCmd = 0;
const char *zBase = g.zRepositoryName;
int isReadonly = 0;
g.zPhase = "process_one_web_page";
#if !defined(_WIN32)
signal(SIGSEGV, sigsegv_handler);
#endif
/* Handle universal query parameters */
|
| ︙ | ︙ | |||
2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 |
jsonOnce = 1;
}
}
#endif
if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
cgi_decode_post_parameters();
if( !cgi_same_origin() ){
db_protect(PROTECT_READONLY);
}
}
if( g.fCgiTrace ){
fossil_trace("######## Calling %s #########\n", pCmd->zName);
cgi_print_all(1, 1);
}
| > | 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 |
jsonOnce = 1;
}
}
#endif
if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
cgi_decode_post_parameters();
if( !cgi_same_origin() ){
isReadonly = 1;
db_protect(PROTECT_READONLY);
}
}
if( g.fCgiTrace ){
fossil_trace("######## Calling %s #########\n", pCmd->zName);
cgi_print_all(1, 1);
}
|
| ︙ | ︙ | |||
2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 |
}
if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags);
}
}
}
#endif
}
/* Return the result.
*/
g.zPhase = "web-page reply";
cgi_reply();
}
| > > > | 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 |
}
if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags);
}
}
}
#endif
if( isReadonly ){
db_protect_pop();
}
}
/* Return the result.
*/
g.zPhase = "web-page reply";
cgi_reply();
}
|
| ︙ | ︙ |