u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
+ u8 bSafeMode; /* True to prohibit unsafe operations */
+ u8 bSafeModePersist; /* The long-term value of bSafeMode */
unsigned statsOn; /* True to display memory stats before each finalize */
unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
int outCount; /* Revert to stdout when reaching zero */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
+ char *zNonce; /* Nonce for temporary safe-mode excapes */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
- ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
+ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
};
sqlite3_result_value(pCtx, apVal[0]);
}
+/*
+** If in safe mode, print an error message described by the arguments
+** and exit immediately.
+*/
+static void failIfSafeMode(
+ ShellState *p,
+ const char *zErrMsg,
+ ...
+){
+ if( p->bSafeMode ){
+ va_list ap;
+ char *zMsg;
+ va_start(ap, zErrMsg);
+ zMsg = sqlite3_vmprintf(zErrMsg, ap);
+ va_end(ap);
+ raw_printf(stderr, "line %d: ", p->lineno);
+ utf8_printf(stderr, "%s\n", zMsg);
+ exit(1);
+ }
+}
+
/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
+/*
+** This authorizer runs in safe mode.
+*/
+static int safeModeAuth(
+ void *pClientData,
+ int op,
+ const char *zA1,
+ const char *zA2,
+ const char *zA3,
+ const char *zA4
+){
+ ShellState *p = (ShellState*)pClientData;
+ static const char *azProhibitedFunctions[] = {
+ "edit",
+ "fts3_tokenizer",
+ "load_extension",
+ "readfile",
+ "writefile",
+ "zipfile",
+ "zipfile_cds",
+ };
+ UNUSED_PARAMETER(zA2);
+ UNUSED_PARAMETER(zA3);
+ UNUSED_PARAMETER(zA4);
+ switch( op ){
+ case SQLITE_ATTACH: {
+ failIfSafeMode(p, "cannot run ATTACH in safe mode");
+ break;
+ }
+ case SQLITE_FUNCTION: {
+ int i;
+ for(i=0; i<ArraySize(azProhibitedFunctions); i++){
+ if( sqlite3_stricmp(zA1, azProhibitedFunctions[i])==0 ){
+ failIfSafeMode(p, "cannot use the %s() function in safe mode",
+ azProhibitedFunctions[i]);
+ }
+ }
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
/*
** When the ".auth ON" is set, the following authorizer callback is
** invoked. It always returns SQLITE_OK.
}
}
raw_printf(p->out, "\n");
+ if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
#endif
" table ASCII-art table",
" tabs Tab-separated values",
" tcl TCL list elements",
+ ".nonce STRING Disable safe mode for one command if the nonce matches",
".nullvalue STRING Use STRING in place of NULL values",
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
" If FILE begins with '|' then open as a pipe",
}
#endif
}
+ if( p->bSafeModePersist && p->db!=0 ){
+ sqlite3_set_authorizer(p->db, safeModeAuth, p);
+ }
}
/*
open_db(p, 0);
if( booleanValue(azArg[1]) ){
sqlite3_set_authorizer(p->db, shellAuth, p);
+ }else if( p->bSafeModePersist ){
+ sqlite3_set_authorizer(p->db, safeModeAuth, p);
}else{
sqlite3_set_authorizer(p->db, 0, 0);
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
+ failIfSafeMode(p, "cannot run .archive in safe mode");
rc = arDotCommand(p, 0, azArg, nArg);
}else
#endif
int j;
int bAsync = 0;
const char *zVfs = 0;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
}else
if( c=='c' && strcmp(azArg[0],"cd")==0 ){
+ failIfSafeMode(p, "cannot run .cd in safe mode");
if( nArg==2 ){
#if defined(_WIN32) || defined(WIN32)
wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
}else
if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
+ failIfSafeMode(p, "cannot run .clone in safe mode");
if( nArg==2 ){
tryToClone(p, azArg[1]);
}else{
int nSkip = 0; /* Initial lines to skip */
int useOutputMode = 1; /* Use output mode to determine separators */
+ failIfSafeMode(p, "cannot run .import in safe mode");
memset(&sCtx, 0, sizeof(sCtx));
if( p->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
+ failIfSafeMode(p, "cannot run .load in safe mode");
if( nArg<2 ){
raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
rc = 1;
#endif
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
+ failIfSafeMode(p, "cannot run .log in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .log FILENAME\n");
rc = 1;
p->cMode = p->mode;
}else
+ if( c=='n' && strcmp(azArg[0], "nonce")==0 ){
+ if( nArg!=2 ){
+ raw_printf(stderr, "Usage: .nonce NONCE\n");
+ rc = 1;
+ }else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){
+ raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]);
+ exit(1);
+ }
+ p->bSafeMode = 0;
+ return 0; /* Return immediately to bypass the safe mode reset
+ ** at the end of this procedure */
+ }else
+
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
}
/* If a filename is specified, try to open it first */
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
- if( newFlag ) shellDeleteFile(zNewFilename);
+ if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename);
+ if( p->bSafeMode
+ && p->openMode!=SHELL_OPEN_HEXDB
+ && zNewFilename
+ && strcmp(zNewFilename,":memory:")!=0
+ ){
+ failIfSafeMode(p, "cannot open disk-based database files in safe mode");
+ }
p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
int bBOM = 0;
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
bOnce = 2;
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
+ failIfSafeMode(p, "cannot run .read in safe mode");
if( nArg!=2 ){
raw_printf(stderr, "Usage: .read FILE\n");
rc = 1;
sqlite3_backup *pBackup;
int nTimeout = 0;
+ failIfSafeMode(p, "cannot run .restore in safe mode");
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
*/
if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
FILE *out = 0;
+ failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]);
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
){
char *zCmd;
int i, x;
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( nArg<2 ){
raw_printf(stderr, "Usage: .system COMMAND\n");
rc = 1;
p->outCount--;
if( p->outCount==0 ) output_reset(p);
}
+ p->bSafeMode = p->bSafeModePersist;
return rc;
}
}else{
clearTempFile(p);
}
+ p->bSafeMode = p->bSafeModePersist;
}else if( nSql && _all_whitespace(zSql) ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
nSql = 0;
#endif
" -newline SEP set output row separator. Default: '\\n'\n"
" -nofollow refuse to open symbolic links to database files\n"
+ " -nonce STRING set the safe-mode escape nonce\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
+ " -safe enable safe-mode\n"
" -separator SEP set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
" -sorterref SIZE sorter references threshold size\n"
sqlite3MemTraceActivate(stderr);
}else if( strcmp(z,"-bail")==0 ){
bail_on_error = 1;
+ }else if( strcmp(z,"-nonce")==0 ){
+ free(data.zNonce);
+ data.zNonce = strdup(argv[++i]);
+ }else if( strcmp(z,"-safe")==0 ){
+ /* no-op - catch this on the second pass */
}
}
verify_uninitialized();
i+=2;
}else if( strcmp(z,"-threadsafe")==0 ){
i+=2;
+ }else if( strcmp(z,"-nonce")==0 ){
+ i += 2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
}else if( strcmp(z,"-memtrace")==0 ){
readStdin = 0;
break;
#endif
+ }else if( strcmp(z,"-safe")==0 ){
+ data.bSafeMode = data.bSafeModePersist = 1;
}else{
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
raw_printf(stderr,"Use -help for a list of options.\n");
free(argvToFree);
#endif
free(data.colWidth);
+ free(data.zNonce);
/* Clear the global data structure so that valgrind will detect memory
** leaks */
memset(&data, 0, sizeof(data));