Fossil

Check-in [be0d95aded]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhance the db_prepare() and db_static_prepare() utility routines so that they throw an error if handed more than one SQL statement. This might help prevent SQL injection attacks.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | sec2020
Files: files | file ages | folders
SHA3-256: be0d95adedbc7ce63ec12a7774903f4124b2c1c70b431af6bbb61ff94964b7be
User & Date: drh 2020-08-17 18:57:42.876
Context
2020-08-17
19:59
Every database connection now has a default authorizer, which calls out to an operation-specific authorizer if needed. check-in: f98ef3c103 user: drh tags: sec2020
18:57
Enhance the db_prepare() and db_static_prepare() utility routines so that they throw an error if handed more than one SQL statement. This might help prevent SQL injection attacks. check-in: be0d95aded user: drh tags: sec2020
18:20
Merge in reject-ckout-db branch. check-in: 8c16884aa2 user: stephan tags: sec2020
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
67
68
69
70
71
72
73

74
75
76
77
78
79
80
*/
#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0, 0}
#endif /* INTERFACE */
const struct Stmt empty_Stmt = empty_Stmt_m;

/*
** Call this routine when a database error occurs.

*/
static void db_err(const char *zFormat, ...){
  va_list ap;
  char *z;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);







>







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
*/
#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0, 0}
#endif /* INTERFACE */
const struct Stmt empty_Stmt = empty_Stmt_m;

/*
** Call this routine when a database error occurs.
** This routine throws a fatal error.  It does not return.
*/
static void db_err(const char *zFormat, ...){
  va_list ap;
  char *z;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
361
362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377
378


379
380
381
382
383
384
385
** If the input string contains multiple SQL statements, only the first
** one is processed.  All statements beyond the first are silently ignored.
*/
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
  int rc;
  int prepFlags = 0;
  char *zSql;

  blob_zero(&pStmt->sql);
  blob_vappendf(&pStmt->sql, zFormat, ap);
  va_end(ap);
  zSql = blob_str(&pStmt->sql);
  db.nPrepare++;
  if( flags & DB_PREPARE_PERSISTENT ){
    prepFlags = SQLITE_PREPARE_PERSISTENT;
  }
  rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
  if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
    db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);


  }
  pStmt->pNext = db.pAllStmt;
  pStmt->pPrev = 0;
  if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
  db.pAllStmt = pStmt;
  pStmt->nStep = 0;
  pStmt->rc = rc;







>








|


>
>







362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
** If the input string contains multiple SQL statements, only the first
** one is processed.  All statements beyond the first are silently ignored.
*/
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
  int rc;
  int prepFlags = 0;
  char *zSql;
  const char *zExtra = 0;
  blob_zero(&pStmt->sql);
  blob_vappendf(&pStmt->sql, zFormat, ap);
  va_end(ap);
  zSql = blob_str(&pStmt->sql);
  db.nPrepare++;
  if( flags & DB_PREPARE_PERSISTENT ){
    prepFlags = SQLITE_PREPARE_PERSISTENT;
  }
  rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
  if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
    db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
  }else if( zExtra && !fossil_all_whitespace(zExtra) ){
    db_err("surplus text follows SQL: \"%s\"", zExtra);
  }
  pStmt->pNext = db.pAllStmt;
  pStmt->pPrev = 0;
  if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
  db.pAllStmt = pStmt;
  pStmt->nStep = 0;
  pStmt->rc = rc;
638
639
640
641
642
643
644

645
646
647
648
649
650
651
652
653
654

















655
656
657
658
659
660
661
  rc = db_reset(pStmt);
  db_check_result(rc, pStmt);
  return rc;
}

/*
** COMMAND: test-db-exec-error

**
** Invoke the db_exec() interface with an erroneous SQL statement
** in order to verify the error handling logic.
*/
void db_test_db_exec_cmd(void){
  Stmt err;
  db_find_and_open_repository(0,0);
  db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);");
  db_exec(&err);
}


















/*
** Print the output of one or more SQL queries on standard output.
** This routine is used for debugging purposes only.
*/
int db_debug(const char *zSql, ...){
  Blob sql;







>










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
  rc = db_reset(pStmt);
  db_check_result(rc, pStmt);
  return rc;
}

/*
** COMMAND: test-db-exec-error
** Usage: %fossil test-db-exec-error
**
** Invoke the db_exec() interface with an erroneous SQL statement
** in order to verify the error handling logic.
*/
void db_test_db_exec_cmd(void){
  Stmt err;
  db_find_and_open_repository(0,0);
  db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);");
  db_exec(&err);
}

/*
** COMMAND: test-db-prepare
** Usage: %fossil test-db-prepare ?OPTIONS? SQL
**
** Invoke db_prepare() on the SQL input.  Report any errors encountered.
** This command is used to verify error detection logic in the db_prepare()
** utility routine.
*/
void db_test_db_prepare(void){
  Stmt err;
  db_find_and_open_repository(0,0);
  verify_all_options();
  if( g.argc!=3 ) usage("?OPTIONS? SQL");
  db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/);
  db_finalize(&err);
}

/*
** Print the output of one or more SQL queries on standard output.
** This routine is used for debugging purposes only.
*/
int db_debug(const char *zSql, ...){
  Blob sql;