@@ -355,44 +355,99 @@
355 355 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** Each of these routines pushes the previous protection mask onto
356 356 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** a finite-size stack. Each should be followed by a call to
357 357 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** db_protect_pop() to pop the stack and restore the protections that
358 358 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** existed prior to the call. The protection mask stack has a limited
359 359 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** depth, so take care not to next calls too deeply.
360 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
361 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** About Database Write Protection
362 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** -------------------------------
363 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
364 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** This is *not* a primary means of defending the application from
365 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** attack. Fossil should be secure even if this mechanism is disabled.
366 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** The purpose of database write protection is to provide an additional
367 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** layer of defense in case SQL injection bugs somehow slip into other
368 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** parts of the system. In other words, database write protection is
369 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** not primary defense but rather defense in depth.
370 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
371 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** This mechanism mostly focuses on the USER table, to prevent an
372 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** attacker from giving themselves Admin privilegs, and on the
373 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** CONFIG table and specially "sensitive" settings such as
374 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** "diff-command" or "editor" that if compromised by an attacker
375 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** could lead to an RCE.
376 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
377 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** By default, the USER and CONFIG tables are read-only. Various
378 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** subsystems that legitimately need to change those tables can
379 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** temporarily do so using:
380 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
381 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** db_unprotect(PROTECT_xxx);
382 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** // make the legitmate changes here
383 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** db_protect_pop();
384 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
385 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** Code that runs inside of reduced protections should be carefully
386 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** reviewed to ensure that it is harmless and not subject to SQL
387 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** injection.
388 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
389 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** Read-only operations (such as many web pages like /timeline)
390 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** can invoke db_protect(PROTECT_ALL) to effectively make the database
391 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** read-only. TEMP tables (which are often used for these kinds of
392 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** pages) are still writable, however.
393 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
394 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** The PROTECT_SENSITIVE protection is a subset of PROTECT_CONFIG
395 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** that blocks changes to all of the global_config table, but only
396 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** "sensitive" settings in the config table. PROTECT_SENSITIVE
397 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** relies on triggers and the protected_setting() SQL function to
398 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** prevent changes to sensitive settings.
399 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
400 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** Additional Notes
401 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** ----------------
402 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ **
403 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** Calls to routines like db_set() and db_unset() temporarily disable
404 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** the PROTECT_CONFIG protection. The assumption is that these calls
405 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** cannot be invoked by an SQL injection and are thus safe. Make sure
406 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** this is the case by always using a string literal as the name argument
407 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** to db_set() and db_unset() and friend, not a variable that might
408 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** be compromised by an attack.
360 409 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
*/
361 410 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
void db_protect_only(unsigned flags){
362 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if( db.nProtect>=count(db.aProtect) ){
363 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- fossil_fatal("too many db_protect() calls");
411 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if( db.nProtect>=count(db.aProtect)-2 ){
412 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ fossil_panic("too many db_protect() calls");
364 413 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
365 414 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db.aProtect[db.nProtect++] = db.protectMask;
366 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if( (flags & PROTECT_SENSITIVE)!=0
367 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- && (db.protectMask & PROTECT_SENSITIVE)==0
368 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- && db.bProtectTriggers==0
369 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- ){
415 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if( (flags & PROTECT_SENSITIVE)!=0 && db.bProtectTriggers==0 ){
416 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ /* Create the triggers needed to protect sensitive settings from
417 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** being created or modified the first time that PROTECT_SENSITIVE
418 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** is enabled. Deleting a sensitive setting is harmless, so there
419 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** is not trigger to block deletes. After being created once, the
420 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** triggers persist for the life of the database connection. */
370 421 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db_multi_exec(
371 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- "CREATE TEMP TRIGGER IF NOT EXISTS protect_1"
372 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- " BEFORE INSERT ON config WHEN protected_setting(new.name)"
373 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- " BEGIN SELECT raise(abort,'not authorized'); END;\n"
374 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- "CREATE TEMP TRIGGER IF NOT EXISTS protect_2"
375 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- " BEFORE UPDATE ON config WHEN protected_setting(new.name)"
376 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- " BEGIN SELECT raise(abort,'not authorized'); END;\n"
422 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config"
423 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ " WHEN protected_setting(new.name) BEGIN"
424 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ " SELECT raise(abort,'not authorized');"
425 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "END;\n"
426 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config"
427 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ " WHEN protected_setting(new.name) BEGIN"
428 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ " SELECT raise(abort,'not authorized');"
429 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "END;\n"
377 430 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
);
378 431 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db.bProtectTriggers = 1;
379 432 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
380 433 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db.protectMask = flags;
381 434 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
382 435 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
void db_protect(unsigned flags){
383 436 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db_protect_only(db.protectMask | flags);
384 437 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
385 438 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
void db_unprotect(unsigned flags){
386 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if( db.nProtect>=count(db.aProtect) ){
387 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- fossil_fatal("too many db_unprotect() calls");
439 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if( db.nProtect>=count(db.aProtect)-2 ){
440 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ fossil_panic("too many db_unprotect() calls");
388 441 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
389 442 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db.aProtect[db.nProtect++] = db.protectMask;
390 443 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db.protectMask &= ~flags;
391 444 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
392 445 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
void db_protect_pop(void){
393 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if( db.nProtect<1 ) fossil_fatal("too many db_protect_pop() calls");
446 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if( db.nProtect<1 ){
447 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ fossil_panic("too many db_protect_pop() calls");
448 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
394 449 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
db.protectMask = db.aProtect[--db.nProtect];
395 450 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
396 451 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
397 452 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
/*
398 453 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** Verify that the desired database write pertections are in place.
@@ -409,11 +464,11 @@
409 464 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** Every Fossil database connection automatically registers the following
410 465 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** overarching authenticator callback, and leaves it registered for the
411 466 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** duration of the connection. This authenticator will call any
412 467 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
** sub-authenticators that are registered using db_set_authorizer().
413 468 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
*/
414 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- static int db_top_authorizer(
469 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ int db_top_authorizer(
415 470 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
void *pNotUsed,
416 471 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
int eCode,
417 472 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
const char *z0,
418 473 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
const char *z1,
419 474 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
const char *z2,
@@ -439,10 +494,12 @@
439 494 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
rc = SQLITE_DENY;
440 495 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
441 496 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
break;
442 497 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
443 498 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
case SQLITE_DROP_TEMP_TRIGGER: {
499 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ /* Do not allow the triggers that enforce PROTECT_SENSITIVE
500 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ** to be dropped */
444 501 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
rc = SQLITE_DENY;
445 502 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
break;
446 503 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
447 504 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
448 505 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if( db.xAuth && rc==SQLITE_OK ){
449 506 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!