Fossil SCM
Every database connection now has a default authorizer, which calls out to an operation-specific authorizer if needed.
Commit
f98ef3c1034e1d8d5174c4d8d11448586a049a2ef3eacbf8a3b2701d28ce6ab3
Parent
be0d95adedbc7ce…
2 files changed
+46
-5
+1
-2
M
src/db.c
+46
-5
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -134,10 +134,13 @@ | ||
| 134 | 134 | const char *zStartFile; /* File in which transaction was started */ |
| 135 | 135 | int iStartLine; /* Line of zStartFile where transaction started */ |
| 136 | 136 | int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); |
| 137 | 137 | void *pAuthArg; /* Argument to the authorizer */ |
| 138 | 138 | const char *zAuthName; /* Name of the authorizer */ |
| 139 | + char protectUser; /* Prevent changes to the USER table */ | |
| 140 | + char protectSensitive; /* Prevent changes to sensitive CONFIG entries */ | |
| 141 | + char protectConfig; /* Prevent any changes to the CONFIG table */ | |
| 139 | 142 | } db = {0, 0, 0, 0, 0, 0, }; |
| 140 | 143 | |
| 141 | 144 | /* |
| 142 | 145 | ** Arrange for the given file to be deleted on a failure. |
| 143 | 146 | */ |
| @@ -320,10 +323,51 @@ | ||
| 320 | 323 | } |
| 321 | 324 | db.aHook[db.nCommitHook].sequence = sequence; |
| 322 | 325 | db.aHook[db.nCommitHook].xHook = x; |
| 323 | 326 | db.nCommitHook++; |
| 324 | 327 | } |
| 328 | + | |
| 329 | +/* | |
| 330 | +** Every Fossil database connection automatically registers the following | |
| 331 | +** overarching authenticator callback, and leaves it registered for the | |
| 332 | +** duration of the connection. This authenticator will call any | |
| 333 | +** sub-authenticators that are registered using db_set_authorizer(). | |
| 334 | +*/ | |
| 335 | +static int db_top_authorizer( | |
| 336 | + void *pNotUsed, | |
| 337 | + int eCode, | |
| 338 | + const char *z0, | |
| 339 | + const char *z1, | |
| 340 | + const char *z2, | |
| 341 | + const char *z3 | |
| 342 | +){ | |
| 343 | + int rc = SQLITE_OK; | |
| 344 | + switch( eCode ){ | |
| 345 | + case SQLITE_INSERT: | |
| 346 | + case SQLITE_UPDATE: | |
| 347 | + case SQLITE_DELETE: { | |
| 348 | + if( db.protectUser && sqlite3_stricmp(z0,"user")==0 ){ | |
| 349 | + rc = SQLITE_DENY; | |
| 350 | + }else if( db.protectConfig && | |
| 351 | + (sqlite3_stricmp(z0,"config")==0 || | |
| 352 | + sqlite3_stricmp(z0,"global_config")==0) ){ | |
| 353 | + rc = SQLITE_DENY; | |
| 354 | + } | |
| 355 | + break; | |
| 356 | + } | |
| 357 | + case SQLITE_DROP_TEMP_TRIGGER: { | |
| 358 | + if( db.protectSensitive ){ | |
| 359 | + rc = SQLITE_DENY; | |
| 360 | + } | |
| 361 | + break; | |
| 362 | + } | |
| 363 | + } | |
| 364 | + if( db.xAuth && rc==SQLITE_OK ){ | |
| 365 | + rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3); | |
| 366 | + } | |
| 367 | + return rc; | |
| 368 | +} | |
| 325 | 369 | |
| 326 | 370 | /* |
| 327 | 371 | ** Set or unset the query authorizer callback function |
| 328 | 372 | */ |
| 329 | 373 | void db_set_authorizer( |
| @@ -332,23 +376,22 @@ | ||
| 332 | 376 | const char *zName /* for tracing */ |
| 333 | 377 | ){ |
| 334 | 378 | if( db.xAuth ){ |
| 335 | 379 | fossil_panic("multiple active db_set_authorizer() calls"); |
| 336 | 380 | } |
| 337 | - if( g.db ) sqlite3_set_authorizer(g.db, xAuth, pArg); | |
| 338 | 381 | db.xAuth = xAuth; |
| 339 | 382 | db.pAuthArg = pArg; |
| 340 | 383 | db.zAuthName = zName; |
| 341 | 384 | if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName); |
| 342 | 385 | } |
| 343 | 386 | void db_clear_authorizer(void){ |
| 344 | 387 | if( db.zAuthName && g.fSqlTrace ){ |
| 345 | 388 | fossil_trace("-- discontinue authorizer %s\n", db.zAuthName); |
| 346 | 389 | } |
| 347 | - if( g.db ) sqlite3_set_authorizer(g.db, 0, 0); | |
| 348 | 390 | db.xAuth = 0; |
| 349 | 391 | db.pAuthArg = 0; |
| 392 | + db.zAuthName = 0; | |
| 350 | 393 | } |
| 351 | 394 | |
| 352 | 395 | #if INTERFACE |
| 353 | 396 | /* |
| 354 | 397 | ** Possible flags to db_vprepare |
| @@ -902,13 +945,10 @@ | ||
| 902 | 945 | const char *zSql; |
| 903 | 946 | va_list ap; |
| 904 | 947 | |
| 905 | 948 | xdb = db_open(zFileName ? zFileName : ":memory:"); |
| 906 | 949 | sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 907 | - if( db.xAuth ){ | |
| 908 | - sqlite3_set_authorizer(xdb, db.xAuth, db.pAuthArg); | |
| 909 | - } | |
| 910 | 950 | rc = sqlite3_exec(xdb, zSchema, 0, 0, 0); |
| 911 | 951 | if( rc!=SQLITE_OK ){ |
| 912 | 952 | db_err("%s", sqlite3_errmsg(xdb)); |
| 913 | 953 | } |
| 914 | 954 | va_start(ap, zSchema); |
| @@ -1402,10 +1442,11 @@ | ||
| 1402 | 1442 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1403 | 1443 | db_add_aux_functions(db); |
| 1404 | 1444 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1405 | 1445 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1406 | 1446 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1447 | + sqlite3_set_authorizer(db, db_top_authorizer, db); | |
| 1407 | 1448 | return db; |
| 1408 | 1449 | } |
| 1409 | 1450 | |
| 1410 | 1451 | |
| 1411 | 1452 | /* |
| 1412 | 1453 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -134,10 +134,13 @@ | |
| 134 | const char *zStartFile; /* File in which transaction was started */ |
| 135 | int iStartLine; /* Line of zStartFile where transaction started */ |
| 136 | int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); |
| 137 | void *pAuthArg; /* Argument to the authorizer */ |
| 138 | const char *zAuthName; /* Name of the authorizer */ |
| 139 | } db = {0, 0, 0, 0, 0, 0, }; |
| 140 | |
| 141 | /* |
| 142 | ** Arrange for the given file to be deleted on a failure. |
| 143 | */ |
| @@ -320,10 +323,51 @@ | |
| 320 | } |
| 321 | db.aHook[db.nCommitHook].sequence = sequence; |
| 322 | db.aHook[db.nCommitHook].xHook = x; |
| 323 | db.nCommitHook++; |
| 324 | } |
| 325 | |
| 326 | /* |
| 327 | ** Set or unset the query authorizer callback function |
| 328 | */ |
| 329 | void db_set_authorizer( |
| @@ -332,23 +376,22 @@ | |
| 332 | const char *zName /* for tracing */ |
| 333 | ){ |
| 334 | if( db.xAuth ){ |
| 335 | fossil_panic("multiple active db_set_authorizer() calls"); |
| 336 | } |
| 337 | if( g.db ) sqlite3_set_authorizer(g.db, xAuth, pArg); |
| 338 | db.xAuth = xAuth; |
| 339 | db.pAuthArg = pArg; |
| 340 | db.zAuthName = zName; |
| 341 | if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName); |
| 342 | } |
| 343 | void db_clear_authorizer(void){ |
| 344 | if( db.zAuthName && g.fSqlTrace ){ |
| 345 | fossil_trace("-- discontinue authorizer %s\n", db.zAuthName); |
| 346 | } |
| 347 | if( g.db ) sqlite3_set_authorizer(g.db, 0, 0); |
| 348 | db.xAuth = 0; |
| 349 | db.pAuthArg = 0; |
| 350 | } |
| 351 | |
| 352 | #if INTERFACE |
| 353 | /* |
| 354 | ** Possible flags to db_vprepare |
| @@ -902,13 +945,10 @@ | |
| 902 | const char *zSql; |
| 903 | va_list ap; |
| 904 | |
| 905 | xdb = db_open(zFileName ? zFileName : ":memory:"); |
| 906 | sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 907 | if( db.xAuth ){ |
| 908 | sqlite3_set_authorizer(xdb, db.xAuth, db.pAuthArg); |
| 909 | } |
| 910 | rc = sqlite3_exec(xdb, zSchema, 0, 0, 0); |
| 911 | if( rc!=SQLITE_OK ){ |
| 912 | db_err("%s", sqlite3_errmsg(xdb)); |
| 913 | } |
| 914 | va_start(ap, zSchema); |
| @@ -1402,10 +1442,11 @@ | |
| 1402 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1403 | db_add_aux_functions(db); |
| 1404 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1405 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1406 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1407 | return db; |
| 1408 | } |
| 1409 | |
| 1410 | |
| 1411 | /* |
| 1412 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -134,10 +134,13 @@ | |
| 134 | const char *zStartFile; /* File in which transaction was started */ |
| 135 | int iStartLine; /* Line of zStartFile where transaction started */ |
| 136 | int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); |
| 137 | void *pAuthArg; /* Argument to the authorizer */ |
| 138 | const char *zAuthName; /* Name of the authorizer */ |
| 139 | char protectUser; /* Prevent changes to the USER table */ |
| 140 | char protectSensitive; /* Prevent changes to sensitive CONFIG entries */ |
| 141 | char protectConfig; /* Prevent any changes to the CONFIG table */ |
| 142 | } db = {0, 0, 0, 0, 0, 0, }; |
| 143 | |
| 144 | /* |
| 145 | ** Arrange for the given file to be deleted on a failure. |
| 146 | */ |
| @@ -320,10 +323,51 @@ | |
| 323 | } |
| 324 | db.aHook[db.nCommitHook].sequence = sequence; |
| 325 | db.aHook[db.nCommitHook].xHook = x; |
| 326 | db.nCommitHook++; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | ** Every Fossil database connection automatically registers the following |
| 331 | ** overarching authenticator callback, and leaves it registered for the |
| 332 | ** duration of the connection. This authenticator will call any |
| 333 | ** sub-authenticators that are registered using db_set_authorizer(). |
| 334 | */ |
| 335 | static int db_top_authorizer( |
| 336 | void *pNotUsed, |
| 337 | int eCode, |
| 338 | const char *z0, |
| 339 | const char *z1, |
| 340 | const char *z2, |
| 341 | const char *z3 |
| 342 | ){ |
| 343 | int rc = SQLITE_OK; |
| 344 | switch( eCode ){ |
| 345 | case SQLITE_INSERT: |
| 346 | case SQLITE_UPDATE: |
| 347 | case SQLITE_DELETE: { |
| 348 | if( db.protectUser && sqlite3_stricmp(z0,"user")==0 ){ |
| 349 | rc = SQLITE_DENY; |
| 350 | }else if( db.protectConfig && |
| 351 | (sqlite3_stricmp(z0,"config")==0 || |
| 352 | sqlite3_stricmp(z0,"global_config")==0) ){ |
| 353 | rc = SQLITE_DENY; |
| 354 | } |
| 355 | break; |
| 356 | } |
| 357 | case SQLITE_DROP_TEMP_TRIGGER: { |
| 358 | if( db.protectSensitive ){ |
| 359 | rc = SQLITE_DENY; |
| 360 | } |
| 361 | break; |
| 362 | } |
| 363 | } |
| 364 | if( db.xAuth && rc==SQLITE_OK ){ |
| 365 | rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3); |
| 366 | } |
| 367 | return rc; |
| 368 | } |
| 369 | |
| 370 | /* |
| 371 | ** Set or unset the query authorizer callback function |
| 372 | */ |
| 373 | void db_set_authorizer( |
| @@ -332,23 +376,22 @@ | |
| 376 | const char *zName /* for tracing */ |
| 377 | ){ |
| 378 | if( db.xAuth ){ |
| 379 | fossil_panic("multiple active db_set_authorizer() calls"); |
| 380 | } |
| 381 | db.xAuth = xAuth; |
| 382 | db.pAuthArg = pArg; |
| 383 | db.zAuthName = zName; |
| 384 | if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName); |
| 385 | } |
| 386 | void db_clear_authorizer(void){ |
| 387 | if( db.zAuthName && g.fSqlTrace ){ |
| 388 | fossil_trace("-- discontinue authorizer %s\n", db.zAuthName); |
| 389 | } |
| 390 | db.xAuth = 0; |
| 391 | db.pAuthArg = 0; |
| 392 | db.zAuthName = 0; |
| 393 | } |
| 394 | |
| 395 | #if INTERFACE |
| 396 | /* |
| 397 | ** Possible flags to db_vprepare |
| @@ -902,13 +945,10 @@ | |
| 945 | const char *zSql; |
| 946 | va_list ap; |
| 947 | |
| 948 | xdb = db_open(zFileName ? zFileName : ":memory:"); |
| 949 | sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 950 | rc = sqlite3_exec(xdb, zSchema, 0, 0, 0); |
| 951 | if( rc!=SQLITE_OK ){ |
| 952 | db_err("%s", sqlite3_errmsg(xdb)); |
| 953 | } |
| 954 | va_start(ap, zSchema); |
| @@ -1402,10 +1442,11 @@ | |
| 1442 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1443 | db_add_aux_functions(db); |
| 1444 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1445 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1446 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1447 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1448 | return db; |
| 1449 | } |
| 1450 | |
| 1451 | |
| 1452 | /* |
| 1453 |
+1
-2
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -451,16 +451,15 @@ | ||
| 451 | 451 | db_multi_exec( |
| 452 | 452 | "DROP TABLE IF EXISTS ticket;" |
| 453 | 453 | "DROP TABLE IF EXISTS ticketchng;" |
| 454 | 454 | ); |
| 455 | 455 | zSql = ticket_table_schema(); |
| 456 | + db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); | |
| 456 | 457 | if( separateConnection ){ |
| 457 | 458 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 458 | - db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); | |
| 459 | 459 | db_init_database(g.zRepositoryName, zSql, 0); |
| 460 | 460 | }else{ |
| 461 | - db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); | |
| 462 | 461 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 463 | 462 | } |
| 464 | 463 | db_clear_authorizer(); |
| 465 | 464 | fossil_free(zSql); |
| 466 | 465 | } |
| 467 | 466 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -451,16 +451,15 @@ | |
| 451 | db_multi_exec( |
| 452 | "DROP TABLE IF EXISTS ticket;" |
| 453 | "DROP TABLE IF EXISTS ticketchng;" |
| 454 | ); |
| 455 | zSql = ticket_table_schema(); |
| 456 | if( separateConnection ){ |
| 457 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 458 | db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); |
| 459 | db_init_database(g.zRepositoryName, zSql, 0); |
| 460 | }else{ |
| 461 | db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); |
| 462 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 463 | } |
| 464 | db_clear_authorizer(); |
| 465 | fossil_free(zSql); |
| 466 | } |
| 467 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -451,16 +451,15 @@ | |
| 451 | db_multi_exec( |
| 452 | "DROP TABLE IF EXISTS ticket;" |
| 453 | "DROP TABLE IF EXISTS ticketchng;" |
| 454 | ); |
| 455 | zSql = ticket_table_schema(); |
| 456 | db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); |
| 457 | if( separateConnection ){ |
| 458 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 459 | db_init_database(g.zRepositoryName, zSql, 0); |
| 460 | }else{ |
| 461 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 462 | } |
| 463 | db_clear_authorizer(); |
| 464 | fossil_free(zSql); |
| 465 | } |
| 466 |