Skip to content

Commit 1c90ee9

Browse files
author
D. Richard Hipp
committed
When doing an SQLAR archive extraction in the CLI, postpone creating symlinks until after
all files and directories have been created. This prevents a hostile archive from creating a symlink through which it can subsequently write content outside of the target directory. [forum:forumpost/9e176adfef91c207|Forum post 9e176adfef91c207].
1 parent b302111 commit 1c90ee9

File tree

1 file changed

+20
-12
lines changed

1 file changed

+20
-12
lines changed

src/shell.c.in

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6665,11 +6665,14 @@ static int arRemoveCommand(ArCommand *pAr){
66656665
*/
66666666
static int arExtractCommand(ArCommand *pAr){
66676667
const char *zSql1 =
6668-
"SELECT "
6669-
" ($dir || name),"
6670-
" writefile(($dir || name), %s, mode, mtime) "
6671-
"FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)"
6672-
" AND name NOT GLOB '*..[/\\]*'";
6668+
"SELECT ($dir || name),\n"
6669+
" writefile(($dir || name), %s, mode, mtime)\n"
6670+
" FROM %s\n"
6671+
" WHERE (%s)\n"
6672+
" AND NOT ((mode&0xf000)==0x8000 AND $pass>0)\n" /* Files on pass 0 */
6673+
" AND NOT (data IS NULL AND $pass==1)\n" /* Dirs on passes 0,2 */
6674+
" AND NOT ((mode&0xf000)==0xa000 AND $pass<>1)\n" /* Symlink on pass 1 */
6675+
" AND name NOT GLOB '*..[/\\]*'\n";
66736676

66746677
const char *azExtraArg[] = {
66756678
"sqlar_uncompress(data, sz)",
@@ -6705,16 +6708,21 @@ static int arExtractCommand(ArCommand *pAr){
67056708
j = sqlite3_bind_parameter_index(pSql, "$dir");
67066709
sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC);
67076710

6708-
/* Run the SELECT statement twice. The first time, writefile() is called
6709-
** for all archive members that should be extracted. The second time,
6710-
** only for the directories. This is because the timestamps for
6711-
** extracted directories must be reset after they are populated (as
6712-
** populating them changes the timestamp). */
6713-
for(i=0; i<2; i++){
6714-
j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
6711+
/* Run the SELECT statement thrice:
6712+
** (1) writefile() all files and directories, but not symlinks
6713+
** (2) writefile() for symlinks
6714+
** (3) writefile() for directory again
6715+
** Symlinks are created after everything else to prevent writing content
6716+
** through a symlink that was created by the extraction.
6717+
** The third pass is so that the timestamps for extracted directories
6718+
** will be reset to the value in the archive, since populating them
6719+
** in the previous passes will have changed the timestamp. */
6720+
for(i=0; i<3; i++){
6721+
j = sqlite3_bind_parameter_index(pSql, "$pass");
67156722
sqlite3_bind_int(pSql, j, i);
67166723
if( pAr->bDryRun ){
67176724
cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql));
6725+
break;
67186726
}else{
67196727
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
67206728
if( i==0 && pAr->bVerbose ){

0 commit comments

Comments
 (0)