Fossil

Check-in [f98ef3c103]
Login

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

Overview
Comment:Every database connection now has a default authorizer, which calls out to an operation-specific authorizer if needed.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | sec2020
Files: files | file ages | folders
SHA3-256: f98ef3c1034e1d8d5174c4d8d11448586a049a2ef3eacbf8a3b2701d28ce6ab3
User & Date: drh 2020-08-17 19:59:55.907
Context
2020-08-17
20:03
Identify security-sensitive settings. check-in: 3bccd7fff2 user: drh tags: sec2020
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
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
132
133
134
135
136
137
138



139
140
141
142
143
144
145
  int nBeforeCommit;        /* Number of entries in azBeforeCommit */
  int nPriorChanges;        /* sqlite3_total_changes() at transaction start */
  const char *zStartFile;   /* File in which transaction was started */
  int iStartLine;           /* Line of zStartFile where transaction started */
  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
  void *pAuthArg;           /* Argument to the authorizer */
  const char *zAuthName;    /* Name of the authorizer */



} db = {0, 0, 0, 0, 0, 0, };

/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );







>
>
>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  int nBeforeCommit;        /* Number of entries in azBeforeCommit */
  int nPriorChanges;        /* sqlite3_total_changes() at transaction start */
  const char *zStartFile;   /* File in which transaction was started */
  int iStartLine;           /* Line of zStartFile where transaction started */
  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
  void *pAuthArg;           /* Argument to the authorizer */
  const char *zAuthName;    /* Name of the authorizer */
  char protectUser;         /* Prevent changes to the USER table */
  char protectSensitive;    /* Prevent changes to sensitive CONFIG entries */
  char protectConfig;       /* Prevent any changes to the CONFIG table */
} db = {0, 0, 0, 0, 0, 0, };

/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
318
319
320
321
322
323
324









































325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
      db.aHook[i].xHook = xS;
    }
  }
  db.aHook[db.nCommitHook].sequence = sequence;
  db.aHook[db.nCommitHook].xHook = x;
  db.nCommitHook++;
}










































/*
** Set or unset the query authorizer callback function
*/
void db_set_authorizer(
  int(*xAuth)(void*,int,const char*,const char*,const char*,const char*),
  void *pArg,
  const char *zName /* for tracing */
){
  if( db.xAuth ){
    fossil_panic("multiple active db_set_authorizer() calls");
  }
  if( g.db ) sqlite3_set_authorizer(g.db, xAuth, pArg);
  db.xAuth = xAuth;
  db.pAuthArg = pArg;
  db.zAuthName = zName;
  if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName);
}
void db_clear_authorizer(void){
  if( db.zAuthName && g.fSqlTrace ){
    fossil_trace("-- discontinue authorizer %s\n", db.zAuthName);
  }
  if( g.db ) sqlite3_set_authorizer(g.db, 0, 0);
  db.xAuth = 0;
  db.pAuthArg = 0;

}

#if INTERFACE
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR  0x001  /* Suppress errors */







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












<









<


>







321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
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
386
387
388
389

390
391
392
393
394
395
396
397
398
399
      db.aHook[i].xHook = xS;
    }
  }
  db.aHook[db.nCommitHook].sequence = sequence;
  db.aHook[db.nCommitHook].xHook = x;
  db.nCommitHook++;
}

/*
** Every Fossil database connection automatically registers the following
** overarching authenticator callback, and leaves it registered for the
** duration of the connection.  This authenticator will call any
** sub-authenticators that are registered using db_set_authorizer().
*/
static int db_top_authorizer(
  void *pNotUsed,
  int eCode,
  const char *z0,
  const char *z1,
  const char *z2,
  const char *z3
){
  int rc = SQLITE_OK;
  switch( eCode ){
    case SQLITE_INSERT:
    case SQLITE_UPDATE:
    case SQLITE_DELETE: {
      if( db.protectUser && sqlite3_stricmp(z0,"user")==0 ){
        rc = SQLITE_DENY;
      }else if( db.protectConfig &&
               (sqlite3_stricmp(z0,"config")==0 ||
                sqlite3_stricmp(z0,"global_config")==0) ){
        rc = SQLITE_DENY;
      }
      break;
    }
    case SQLITE_DROP_TEMP_TRIGGER: {
      if( db.protectSensitive ){
        rc = SQLITE_DENY;
      }
      break;
    }
  }
  if( db.xAuth && rc==SQLITE_OK ){
    rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3);
  }
  return rc;
}

/*
** Set or unset the query authorizer callback function
*/
void db_set_authorizer(
  int(*xAuth)(void*,int,const char*,const char*,const char*,const char*),
  void *pArg,
  const char *zName /* for tracing */
){
  if( db.xAuth ){
    fossil_panic("multiple active db_set_authorizer() calls");
  }

  db.xAuth = xAuth;
  db.pAuthArg = pArg;
  db.zAuthName = zName;
  if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName);
}
void db_clear_authorizer(void){
  if( db.zAuthName && g.fSqlTrace ){
    fossil_trace("-- discontinue authorizer %s\n", db.zAuthName);
  }

  db.xAuth = 0;
  db.pAuthArg = 0;
  db.zAuthName = 0;
}

#if INTERFACE
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR  0x001  /* Suppress errors */
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
  sqlite3 *xdb;
  int rc;
  const char *zSql;
  va_list ap;

  xdb = db_open(zFileName ? zFileName : ":memory:");
  sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0);
  if( db.xAuth ){
    sqlite3_set_authorizer(xdb, db.xAuth, db.pAuthArg);
  }
  rc = sqlite3_exec(xdb, zSchema, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    db_err("%s", sqlite3_errmsg(xdb));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(xdb, zSql, 0, 0, 0);







<
<
<







943
944
945
946
947
948
949



950
951
952
953
954
955
956
  sqlite3 *xdb;
  int rc;
  const char *zSql;
  va_list ap;

  xdb = db_open(zFileName ? zFileName : ":memory:");
  sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0);



  rc = sqlite3_exec(xdb, zSchema, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    db_err("%s", sqlite3_errmsg(xdb));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(xdb, zSql, 0, 0, 0);
1400
1401
1402
1403
1404
1405
1406

1407
1408
1409
1410
1411
1412
1413
    db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
  );
  if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
  db_add_aux_functions(db);
  re_add_sql_func(db);  /* The REGEXP operator */
  foci_register(db);    /* The "files_of_checkin" virtual table */
  sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);

  return db;
}


/*
** Detaches the zLabel database.
*/







>







1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
    db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
  );
  if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
  db_add_aux_functions(db);
  re_add_sql_func(db);  /* The REGEXP operator */
  foci_register(db);    /* The "files_of_checkin" virtual table */
  sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
  sqlite3_set_authorizer(db, db_top_authorizer, db);
  return db;
}


/*
** Detaches the zLabel database.
*/
Changes to src/tkt.c.
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
464
465
466
467
468
  char *zSql;

  db_multi_exec(
    "DROP TABLE IF EXISTS ticket;"
    "DROP TABLE IF EXISTS ticketchng;"
  );
  zSql = ticket_table_schema();

  if( separateConnection ){
    if( db_transaction_nesting_depth() ) db_end_transaction(0);
    db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
    db_init_database(g.zRepositoryName, zSql, 0);
  }else{
    db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
    db_multi_exec("%s", zSql/*safe-for-%s*/);
  }
  db_clear_authorizer();
  fossil_free(zSql);
}

/*







>


<


<







449
450
451
452
453
454
455
456
457
458

459
460

461
462
463
464
465
466
467
  char *zSql;

  db_multi_exec(
    "DROP TABLE IF EXISTS ticket;"
    "DROP TABLE IF EXISTS ticketchng;"
  );
  zSql = ticket_table_schema();
  db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
  if( separateConnection ){
    if( db_transaction_nesting_depth() ) db_end_transaction(0);

    db_init_database(g.zRepositoryName, zSql, 0);
  }else{

    db_multi_exec("%s", zSql/*safe-for-%s*/);
  }
  db_clear_authorizer();
  fossil_free(zSql);
}

/*